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

Side by Side Diff: watchExtensions.py

Issue 29762573: Issue 6602 - Introduce watchextensions
Patch Set: Created April 26, 2018, 11:03 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tox.ini ('k') | watchdog.ini.example » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 from __future__ import print_function
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6 import argparse
7 from configparser import ConfigParser
8 import io
9 import json
10 import logging
11 import os
12 import shutil
13 import subprocess
14
15 try: # Python 3 only
16 from urllib import request as urllib
17 except ImportError: # Python 2
18 import urllib
19 from xml.etree import ElementTree
20
21 logging.basicConfig(level=logging.INFO)
22
23
24 class ExtensionDiff(object):
25 def __init__(self, ext_name, config, cleanup=True):
26 self.logger = logging.getLogger(name=ext_name)
27
28 self.ext_name = ext_name
29 self.cleanup = cleanup
30
31 self._parse_config(config)
32
33 # properties
34 self._cws_ext_url = None
35 self._current_ext_version = None
36
37 self.downloaded_file = None
38
39 super(ExtensionDiff, self).__init__()
40
41 def _git_cmd(self, cmds, relative=False):
42 base = ['git']
43 if not relative:
44 base += ['-C', self.repo_abs]
45 suffix = []
46 if not any(x in cmds for x in ['status', 'add']):
47 suffix += ['--quiet']
48 return subprocess.check_output(base + list(cmds) + suffix)
49
50 def _assure_local_repository(self):
51 no_file = False
52 if os.path.exists(os.path.join(self.repo_abs, '.git')):
53 try:
54 with io.open(os.path.join(self.repo_abs, '.bb_id'), 'r') as fp:
55 ext_id = fp.read().strip()
56 except IOError:
57 no_file = True
58 if no_file or ext_id != self.ext_id:
59 raise ValueError('Repository {} path exists, but it is not '
60 'our repository'.format(self.repo_abs))
61 else:
62 self.logger.info('Cloning ' + self.tracking_repository)
63 self._git_cmd(['clone', self.tracking_repository, self.repo_abs],
64 relative=True)
65
66 self._git_cmd(['fetch', 'origin'])
67 self._git_cmd(['checkout', 'master'])
68 self._git_cmd(['reset', '--hard', 'origin/master'])
69
70 def _parse_config(self, config):
71 self.local_repository_base = config.get('base', 'repository_root')
72 self.ext_id = config.get('extensions', self.ext_name + '_id')
73 self.tracking_repository = config.get('extensions',
74 self.ext_name + '_repository')
75
76 self.repo_abs = os.path.join(self.local_repository_base, self.ext_name)
77
78 def __enter__(self):
79 return self
80
81 def __exit__(self, exc_type, exc_value, exc_traceback):
82 if exc_value:
83 self.logger.error(exc_value.message)
84 elif self.cleanup:
85 shutil.rmtree(self.repo_abs, ignore_errors=True)
86
87 return True
88
89 def _fetch_latest_chrome_version(self):
90 omaha_url = 'https://omahaproxy.appspot.com/all.json?os=win'
91 response = urllib.urlopen(omaha_url).read()
92
93 if isinstance(response, bytes):
94 data = json.loads(response.decode('utf-8'))
95 else:
96 data = json.loads(response)
97
98 stable = [x for x in data[0]['versions'] if x['channel'] == 'stable']
99 return stable[0]['current_version']
100
101 @property
102 def cws_ext_url(self):
103 if not self._cws_ext_url:
104 service_url = 'https://clients2.google.com/service/update2/crx'
105 ext_url = service_url + '?prodversion={}&x=id%3D{}%26uc'.format(
106 self._fetch_latest_chrome_version(), self.ext_id,
107 )
108 self._cws_ext_url = ext_url
109
110 return self._cws_ext_url
111
112 @property
113 def current_ext_version(self):
114 if not self._current_ext_version:
115 updatecheck_url = self.cws_ext_url + '&response=updatecheck'
116 manifest = urllib.urlopen(updatecheck_url).read()
117 root = ElementTree.fromstring(manifest)
118
119 ns = {'g': 'http://www.google.com/update2/response'}
120 update = root.find('g:app/g:updatecheck', ns)
121
122 self._current_ext_version = update.attrib['version']
123 return self._current_ext_version
124
125 def _download_ext_crx(self):
126 download_url = self.cws_ext_url + '&response=redirect'
127 filename = '{}-{}.crx'.format(self.ext_id, self.current_ext_version)
128
129 urllib.urlretrieve(download_url, filename)
130 self.downloaded_file = filename
131 self.logger.info('Downloaded ' + filename)
132
133 def _unzip_to_repository(self):
134 cmd = ['unzip', '-qq', '-o', self.downloaded_file, '-d', self.repo_abs]
135 try:
136 self.logger.info('Unzipping {} to {}'.format(self.downloaded_file,
137 self.repo_abs))
138 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
139 except subprocess.CalledProcessError as e:
140 # CRX files get zipped with extra-bytes, which causes a warning in
141 # unzip (with returncode 1).
142 if not e.returncode == 1:
143 raise
144
145 def _get_tracked_version(self):
146 return [x.decode() for x in
147 self._git_cmd(['log', '--pretty=format:%s']).splitlines()]
148
149 def _track_new_contents(self, version):
150 status = self._git_cmd(['status'])
151 if b'nothing to commit' not in status:
152 self._git_cmd(['add', '--all'])
153 self._git_cmd(['commit', '-m', version])
154 self._git_cmd(['push', 'origin', 'master'])
155
156 def run(self):
157 self._assure_local_repository()
158
159 next_version = self.current_ext_version
160 if next_version not in self._get_tracked_version():
161 self.logger.info('New untracked version found!')
162 self._download_ext_crx()
163 self._unzip_to_repository()
164 self._track_new_contents(next_version)
165 else:
166 self.logger.info('Nothing to do.')
167
168
169 if __name__ == '__main__':
170 parser = argparse.ArgumentParser()
171 parser.add_argument('-q', '--quiet', help='Suppress informational output',
172 action='store_true', default=False)
173
174 args = parser.parse_args()
175 if args.quiet:
176 logging.disable(logging.INFO)
177
178 paths = [
179 os.path.expanduser('~/watchdog.ini'),
180 '/etc/watchdog.ini',
181 ]
182
183 config = ConfigParser()
184 if not config.read(paths):
185 raise Exception('No configuration file was found. Please provide '
186 '~/watchdog.ini or /etc/watchdog.ini')
187
188 for ext_name in config.options('enabled'):
189 with ExtensionDiff(ext_name, config) as watcher:
190 watcher.run()
OLDNEW
« 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