From 9ced01ebf7f0c4a8961b572f5c14c2c3330f1131 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 14:24:00 -0400 Subject: [PATCH] Add publish script for stable version on Chromium --- dist/chromium/publish-stable.py | 190 ++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100755 dist/chromium/publish-stable.py diff --git a/dist/chromium/publish-stable.py b/dist/chromium/publish-stable.py new file mode 100755 index 000000000..d1adc1474 --- /dev/null +++ b/dist/chromium/publish-stable.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 + +import datetime +import json +import os +import re +import requests +import shutil +import subprocess +import sys +import tempfile +import time +import zipfile + +from string import Template + +# - Download target (raw) uBlock0.chromium.zip from GitHub +# - This is referred to as "raw" package +# - This will fail if not a dev build +# - Upload uBlock0.chromium.zip to Chrome store +# - Publish uBlock0.chromium.zip to Chrome store + +# Find path to project root +projdir = os.path.split(os.path.abspath(__file__))[0] +while not os.path.isdir(os.path.join(projdir, '.git')): + projdir = os.path.normpath(os.path.join(projdir, '..')) + +# We need a version string to work with +if len(sys.argv) >= 2 and sys.argv[1]: + version = sys.argv[1] +else: + version = input('Github release version: ') +version.strip() +if not re.search('^\d+\.\d+\.\d+$', version): + print('Error: Invalid version string.') + exit(1) + +cs_extension_id = 'cjpalhdlnbpafiamejdnhcphjbkeiagm' +tmpdir = tempfile.TemporaryDirectory() +raw_zip_filename = 'uBlock0_' + version + '.chromium.zip' +raw_zip_filepath = os.path.join(tmpdir.name, raw_zip_filename) +github_owner = 'gorhill' +github_repo = 'uBlock' + +# Load/save auth secrets +# The tmp directory is excluded from git +ubo_secrets = dict() +ubo_secrets_filename = os.path.join(projdir, 'tmp', 'ubo_secrets') +if os.path.isfile(ubo_secrets_filename): + with open(ubo_secrets_filename) as f: + ubo_secrets = json.load(f) + +def input_secret(prompt, token): + if token in ubo_secrets: + prompt += ' ✔' + prompt += ': ' + value = input(prompt).strip() + if len(value) == 0: + if token not in ubo_secrets: + print('Token error:', token) + exit(1) + value = ubo_secrets[token] + elif token not in ubo_secrets or value != ubo_secrets[token]: + ubo_secrets[token] = value + exists = os.path.isfile(ubo_secrets_filename) + with open(ubo_secrets_filename, 'w') as f: + json.dump(ubo_secrets, f, indent=2) + if not exists: + os.chmod(ubo_secrets_filename, 0o600) + return value + + +# GitHub API token +github_token = input_secret('Github token', 'github_token') +github_auth = 'token ' + github_token + +# +# Get metadata from GitHub about the release +# + +# https://developer.github.com/v3/repos/releases/#get-a-single-release +print('Downloading release info from GitHub...') +release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, version) +headers = { 'Authorization': github_auth, } +response = requests.get(release_info_url, headers=headers) +if response.status_code != 200: + print('Error: Release not found: {0}'.format(response.status_code)) + exit(1) +release_info = response.json() + +# +# Extract URL to raw package from metadata +# + +# Find url for uBlock0.chromium.zip +raw_zip_url = '' +for asset in release_info['assets']: + if asset['name'] == raw_zip_filename: + raw_zip_url = asset['url'] +if len(raw_zip_url) == 0: + print('Error: Release asset URL not found') + exit(1) + +# +# Download raw package from GitHub +# + +# https://developer.github.com/v3/repos/releases/#get-a-single-release-asset +print('Downloading raw zip package from GitHub...') +headers = { + 'Authorization': github_auth, + 'Accept': 'application/octet-stream', +} +response = requests.get(raw_zip_url, headers=headers) +# Redirections are transparently handled: +# http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history +if response.status_code != 200: + print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code)) + exit(1) +with open(raw_zip_filepath, 'wb') as f: + f.write(response.content) +print('Downloaded raw package saved as {0}'.format(raw_zip_filepath)) + +# +# Upload to Chrome store +# + +# Auth tokens +cs_id = input_secret('Chrome store id', 'cs_id') +cs_secret = input_secret('Chrome store secret', 'cs_secret') +cs_refresh = input_secret('Chrome store refresh token', 'cs_refresh') + +print('Uploading to Chrome store...') +with open(raw_zip_filepath, 'rb') as f: + print('Generating access token...') + auth_url = 'https://accounts.google.com/o/oauth2/token' + auth_payload = { + 'client_id': cs_id, + 'client_secret': cs_secret, + 'grant_type': 'refresh_token', + 'refresh_token': cs_refresh, + } + auth_response = requests.post(auth_url, data=auth_payload) + if auth_response.status_code != 200: + print('Error: Auth failed -- server error {0}'.format(auth_response.status_code)) + print(auth_response.text) + exit(1) + response_dict = auth_response.json() + if 'access_token' not in response_dict: + print('Error: Auth failed -- no access token') + exit(1) + # Prepare access token + cs_auth = 'Bearer ' + response_dict['access_token'] + headers = { + 'Authorization': cs_auth, + 'x-goog-api-version': '2', + } + # Upload + print('Uploading package...') + upload_url = 'https://www.googleapis.com/upload/chromewebstore/v1.1/items/{0}'.format(cs_extension_id) + upload_response = requests.put(upload_url, headers=headers, data=f) + f.close() + if upload_response.status_code != 200: + print('Upload failed -- server error {0}'.format(upload_response.status_code)) + print(upload_response.text) + exit(1) + response_dict = upload_response.json(); + if 'uploadState' not in response_dict or response_dict['uploadState'] != 'SUCCESS': + print('Upload failed -- server error {0}'.format(response_dict['uploadState'])) + exit(1) + print('Upload succeeded.') + # Publish + print('Publishing package...') + publish_url = 'https://www.googleapis.com/chromewebstore/v1.1/items/{0}/publish'.format(cs_extension_id) + headers = { + 'Authorization': cs_auth, + 'x-goog-api-version': '2', + 'Content-Length': '0', + } + publish_response = requests.post(publish_url, headers=headers) + if publish_response.status_code != 200: + print('Error: Chrome store publishing failed -- server error {0}'.format(publish_response.status_code)) + exit(1) + response_dict = publish_response.json(); + if 'status' not in response_dict or response_dict['status'][0] != 'OK': + print('Publishing failed -- server error {0}'.format(response_dict['status'])) + exit(1) + print('Publishing succeeded.') + +print('All done.')