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.

192 lines
6.3 KiB

  1. #!/usr/bin/env python3
  2. import datetime
  3. import json
  4. import jwt
  5. import os
  6. import re
  7. import requests
  8. import shutil
  9. import subprocess
  10. import sys
  11. import tempfile
  12. import time
  13. import zipfile
  14. from distutils.version import StrictVersion
  15. from string import Template
  16. # - Download target (raw) uMatrix.chromium.zip from GitHub
  17. # - This is referred to as "raw" package
  18. # - This will fail if not a dev build
  19. # - Upload uMatrix.chromium.zip to Chrome store
  20. # - Publish uMatrix.chromium.zip to Chrome store
  21. # Find path to project root
  22. projdir = os.path.split(os.path.abspath(__file__))[0]
  23. while not os.path.isdir(os.path.join(projdir, '.git')):
  24. projdir = os.path.normpath(os.path.join(projdir, '..'))
  25. # We need a version string to work with
  26. if len(sys.argv) >= 2 and sys.argv[1]:
  27. version = sys.argv[1]
  28. else:
  29. version = input('Github release version: ')
  30. version.strip()
  31. if not re.search('^\d+\.\d+\.\d+(b|rc)\d+$', version):
  32. print('Error: Invalid version string.')
  33. exit(1)
  34. cs_extension_id = 'eckgcipdkhcfghnmincccnhpdmnbefki'
  35. tmpdir = tempfile.TemporaryDirectory()
  36. raw_zip_filename = 'uMatrix.chromium.zip'
  37. raw_zip_filepath = os.path.join(tmpdir.name, raw_zip_filename)
  38. github_owner = 'gorhill'
  39. github_repo = 'uMatrix'
  40. # Load/save auth secrets
  41. # The build directory is excluded from git
  42. ubo_secrets = dict()
  43. ubo_secrets_filename = os.path.join(projdir, 'dist', 'build', 'ubo_secrets')
  44. if os.path.isfile(ubo_secrets_filename):
  45. with open(ubo_secrets_filename) as f:
  46. ubo_secrets = json.load(f)
  47. def input_secret(prompt, token):
  48. if token in ubo_secrets:
  49. prompt += ''
  50. prompt += ': '
  51. value = input(prompt).strip()
  52. if len(value) == 0:
  53. if token not in ubo_secrets:
  54. print('Token error:', token)
  55. exit(1)
  56. value = ubo_secrets[token]
  57. elif token not in ubo_secrets or value != ubo_secrets[token]:
  58. ubo_secrets[token] = value
  59. exists = os.path.isfile(ubo_secrets_filename)
  60. with open(ubo_secrets_filename, 'w') as f:
  61. json.dump(ubo_secrets, f, indent=2)
  62. if not exists:
  63. os.chmod(ubo_secrets_filename, 0o600)
  64. return value
  65. # GitHub API token
  66. github_token = input_secret('Github token', 'github_token')
  67. github_auth = 'token ' + github_token
  68. #
  69. # Get metadata from GitHub about the release
  70. #
  71. # https://developer.github.com/v3/repos/releases/#get-a-single-release
  72. print('Downloading release info from GitHub...')
  73. release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, version)
  74. headers = { 'Authorization': github_auth, }
  75. response = requests.get(release_info_url, headers=headers)
  76. if response.status_code != 200:
  77. print('Error: Release not found: {0}'.format(response.status_code))
  78. exit(1)
  79. release_info = response.json()
  80. #
  81. # Extract URL to raw package from metadata
  82. #
  83. # Find url for uMatrix.chromium.zip
  84. raw_zip_url = ''
  85. for asset in release_info['assets']:
  86. if asset['name'] == raw_zip_filename:
  87. raw_zip_url = asset['url']
  88. if len(raw_zip_url) == 0:
  89. print('Error: Release asset URL not found')
  90. exit(1)
  91. #
  92. # Download raw package from GitHub
  93. #
  94. # https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
  95. print('Downloading raw zip package from GitHub...')
  96. headers = {
  97. 'Authorization': github_auth,
  98. 'Accept': 'application/octet-stream',
  99. }
  100. response = requests.get(raw_zip_url, headers=headers)
  101. # Redirections are transparently handled:
  102. # http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history
  103. if response.status_code != 200:
  104. print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code))
  105. exit(1)
  106. with open(raw_zip_filepath, 'wb') as f:
  107. f.write(response.content)
  108. print('Downloaded raw package saved as {0}'.format(raw_zip_filepath))
  109. #
  110. # Upload to Chrome store
  111. #
  112. # Auth tokens
  113. cs_id = input_secret('Chrome store id', 'cs_id')
  114. cs_secret = input_secret('Chrome store secret', 'cs_secret')
  115. cs_refresh = input_secret('Chrome store refresh token', 'cs_refresh')
  116. print('Uploading to Chrome store...')
  117. with open(raw_zip_filepath, 'rb') as f:
  118. print('Generating access token...')
  119. auth_url = 'https://accounts.google.com/o/oauth2/token'
  120. auth_payload = {
  121. 'client_id': cs_id,
  122. 'client_secret': cs_secret,
  123. 'grant_type': 'refresh_token',
  124. 'refresh_token': cs_refresh,
  125. }
  126. auth_response = requests.post(auth_url, data=auth_payload)
  127. if auth_response.status_code != 200:
  128. print('Error: Auth failed -- server error {0}'.format(auth_response.status_code))
  129. print(auth_response.text)
  130. exit(1)
  131. response_dict = auth_response.json()
  132. if 'access_token' not in response_dict:
  133. print('Error: Auth failed -- no access token')
  134. exit(1)
  135. # Prepare access token
  136. cs_auth = 'Bearer ' + response_dict['access_token']
  137. headers = {
  138. 'Authorization': cs_auth,
  139. 'x-goog-api-version': '2',
  140. }
  141. # Upload
  142. print('Uploading package...')
  143. upload_url = 'https://www.googleapis.com/upload/chromewebstore/v1.1/items/{0}'.format(cs_extension_id)
  144. upload_response = requests.put(upload_url, headers=headers, data=f)
  145. f.close()
  146. if upload_response.status_code != 200:
  147. print('Upload failed -- server error {0}'.format(upload_response.status_code))
  148. print(upload_response.text)
  149. exit(1)
  150. response_dict = upload_response.json();
  151. if 'uploadState' not in response_dict or response_dict['uploadState'] != 'SUCCESS':
  152. print('Upload failed -- server error {0}'.format(response_dict['uploadState']))
  153. exit(1)
  154. print('Upload succeeded.')
  155. # Publish
  156. print('Publishing package...')
  157. publish_url = 'https://www.googleapis.com/chromewebstore/v1.1/items/{0}/publish'.format(cs_extension_id)
  158. headers = {
  159. 'Authorization': cs_auth,
  160. 'x-goog-api-version': '2',
  161. 'Content-Length': '0',
  162. }
  163. publish_response = requests.post(publish_url, headers=headers)
  164. if publish_response.status_code != 200:
  165. print('Error: Chrome store publishing failed -- server error {0}'.format(publish_response.status_code))
  166. exit(1)
  167. response_dict = publish_response.json();
  168. if 'status' not in response_dict or response_dict['status'][0] != 'OK':
  169. print('Publishing failed -- server error {0}'.format(response_dict['status']))
  170. exit(1)
  171. print('Publishing succeeded.')
  172. print('All done.')