Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: watchExtensions.py

Issue 29762573: Issue 6602 - Introduce watchextensions
Patch Set: Created April 26, 2018, 11:03 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tox.ini ('k') | watchdog.ini.example » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: watchExtensions.py
diff --git a/watchExtensions.py b/watchExtensions.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b73ac176f9d50784c56ab1102dc7f233a303984
--- /dev/null
+++ b/watchExtensions.py
@@ -0,0 +1,190 @@
+from __future__ import print_function
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import argparse
+from configparser import ConfigParser
+import io
+import json
+import logging
+import os
+import shutil
+import subprocess
+
+try: # Python 3 only
+ from urllib import request as urllib
+except ImportError: # Python 2
+ import urllib
+from xml.etree import ElementTree
+
+logging.basicConfig(level=logging.INFO)
+
+
+class ExtensionDiff(object):
+ def __init__(self, ext_name, config, cleanup=True):
+ self.logger = logging.getLogger(name=ext_name)
+
+ self.ext_name = ext_name
+ self.cleanup = cleanup
+
+ self._parse_config(config)
+
+ # properties
+ self._cws_ext_url = None
+ self._current_ext_version = None
+
+ self.downloaded_file = None
+
+ super(ExtensionDiff, self).__init__()
+
+ def _git_cmd(self, cmds, relative=False):
+ base = ['git']
+ if not relative:
+ base += ['-C', self.repo_abs]
+ suffix = []
+ if not any(x in cmds for x in ['status', 'add']):
+ suffix += ['--quiet']
+ return subprocess.check_output(base + list(cmds) + suffix)
+
+ def _assure_local_repository(self):
+ no_file = False
+ if os.path.exists(os.path.join(self.repo_abs, '.git')):
+ try:
+ with io.open(os.path.join(self.repo_abs, '.bb_id'), 'r') as fp:
+ ext_id = fp.read().strip()
+ except IOError:
+ no_file = True
+ if no_file or ext_id != self.ext_id:
+ raise ValueError('Repository {} path exists, but it is not '
+ 'our repository'.format(self.repo_abs))
+ else:
+ self.logger.info('Cloning ' + self.tracking_repository)
+ self._git_cmd(['clone', self.tracking_repository, self.repo_abs],
+ relative=True)
+
+ self._git_cmd(['fetch', 'origin'])
+ self._git_cmd(['checkout', 'master'])
+ self._git_cmd(['reset', '--hard', 'origin/master'])
+
+ def _parse_config(self, config):
+ self.local_repository_base = config.get('base', 'repository_root')
+ self.ext_id = config.get('extensions', self.ext_name + '_id')
+ self.tracking_repository = config.get('extensions',
+ self.ext_name + '_repository')
+
+ self.repo_abs = os.path.join(self.local_repository_base, self.ext_name)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_traceback):
+ if exc_value:
+ self.logger.error(exc_value.message)
+ elif self.cleanup:
+ shutil.rmtree(self.repo_abs, ignore_errors=True)
+
+ return True
+
+ def _fetch_latest_chrome_version(self):
+ omaha_url = 'https://omahaproxy.appspot.com/all.json?os=win'
+ response = urllib.urlopen(omaha_url).read()
+
+ if isinstance(response, bytes):
+ data = json.loads(response.decode('utf-8'))
+ else:
+ data = json.loads(response)
+
+ stable = [x for x in data[0]['versions'] if x['channel'] == 'stable']
+ return stable[0]['current_version']
+
+ @property
+ def cws_ext_url(self):
+ if not self._cws_ext_url:
+ service_url = 'https://clients2.google.com/service/update2/crx'
+ ext_url = service_url + '?prodversion={}&x=id%3D{}%26uc'.format(
+ self._fetch_latest_chrome_version(), self.ext_id,
+ )
+ self._cws_ext_url = ext_url
+
+ return self._cws_ext_url
+
+ @property
+ def current_ext_version(self):
+ if not self._current_ext_version:
+ updatecheck_url = self.cws_ext_url + '&response=updatecheck'
+ manifest = urllib.urlopen(updatecheck_url).read()
+ root = ElementTree.fromstring(manifest)
+
+ ns = {'g': 'http://www.google.com/update2/response'}
+ update = root.find('g:app/g:updatecheck', ns)
+
+ self._current_ext_version = update.attrib['version']
+ return self._current_ext_version
+
+ def _download_ext_crx(self):
+ download_url = self.cws_ext_url + '&response=redirect'
+ filename = '{}-{}.crx'.format(self.ext_id, self.current_ext_version)
+
+ urllib.urlretrieve(download_url, filename)
+ self.downloaded_file = filename
+ self.logger.info('Downloaded ' + filename)
+
+ def _unzip_to_repository(self):
+ cmd = ['unzip', '-qq', '-o', self.downloaded_file, '-d', self.repo_abs]
+ try:
+ self.logger.info('Unzipping {} to {}'.format(self.downloaded_file,
+ self.repo_abs))
+ subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ # CRX files get zipped with extra-bytes, which causes a warning in
+ # unzip (with returncode 1).
+ if not e.returncode == 1:
+ raise
+
+ def _get_tracked_version(self):
+ return [x.decode() for x in
+ self._git_cmd(['log', '--pretty=format:%s']).splitlines()]
+
+ def _track_new_contents(self, version):
+ status = self._git_cmd(['status'])
+ if b'nothing to commit' not in status:
+ self._git_cmd(['add', '--all'])
+ self._git_cmd(['commit', '-m', version])
+ self._git_cmd(['push', 'origin', 'master'])
+
+ def run(self):
+ self._assure_local_repository()
+
+ next_version = self.current_ext_version
+ if next_version not in self._get_tracked_version():
+ self.logger.info('New untracked version found!')
+ self._download_ext_crx()
+ self._unzip_to_repository()
+ self._track_new_contents(next_version)
+ else:
+ self.logger.info('Nothing to do.')
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-q', '--quiet', help='Suppress informational output',
+ action='store_true', default=False)
+
+ args = parser.parse_args()
+ if args.quiet:
+ logging.disable(logging.INFO)
+
+ paths = [
+ os.path.expanduser('~/watchdog.ini'),
+ '/etc/watchdog.ini',
+ ]
+
+ config = ConfigParser()
+ if not config.read(paths):
+ raise Exception('No configuration file was found. Please provide '
+ '~/watchdog.ini or /etc/watchdog.ini')
+
+ for ext_name in config.options('enabled'):
+ with ExtensionDiff(ext_name, config) as watcher:
+ watcher.run()
« no previous file with comments | « tox.ini ('k') | watchdog.ini.example » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld