Legoktm has uploaded a new change for review.
https://gerrit.wikimedia.org/r/169309
Change subject: Refactor into a proper class
......................................................................
Refactor into a proper class
Change-Id: If1d283b02ee72fae1709bba80755a342e684c320
---
M nightly.py
1 file changed, 206 insertions(+), 228 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/labs/tools/extdist
refs/changes/09/169309/1
diff --git a/nightly.py b/nightly.py
index ec0a6a6..cd5a5d4 100644
--- a/nightly.py
+++ b/nightly.py
@@ -19,242 +19,220 @@
import logging
import os
import subprocess
-import sys
import urllib
-# Load our config from JSON
-if os.path.exists('/etc/extdist.conf'):
- with open('/etc/extdist.conf', 'r') as f:
- conf = json.load(f)
-elif os.path.exists(os.path.join(os.path.dirname(__file__), 'conf.json')):
- with open(os.path.join(os.path.dirname(__file__), 'conf.json'), 'r') as f:
- conf = json.load(f)
-else:
- print 'extdist is not configured properly.'
- quit()
-
-class AttrDict(dict):
- def __init__(self, *args, **kwargs):
- super(AttrDict, self).__init__(*args, **kwargs)
- self.__dict__ = self
-
-# Allow accessing settings as attributes
-conf = AttrDict(conf)
-conf.EXT_PATH = os.path.join(conf.SRC_PATH, 'extensions')
-
-# Set up logging
-logging.basicConfig(
- filename=conf.LOG_FILE,
- level=logging.DEBUG,
- format='%(asctime)s %(levelname)s:%(message)s'
-)
-
-
-def check_pid(pid):
- """
- Checks whether the given pid is running
- """
- try:
- # This doesn't actually kill it, just checks if it is running
- os.kill(pid, 0)
- except OSError:
- # Not running
- return False
- else:
- # So it must be running
- return True
-
-
-def create_pid_file():
- """
- Creates a pid file with the current pid
- """
- with open(conf.PID_FILE, 'w') as f:
- f.write(str(os.getpid()))
- logging.info('Creating pid file')
-
-# Check to make sure nightly.py isn't already running
-pid = os.getpid()
-if os.path.exists(conf.PID_FILE):
- with open(conf.PID_FILE, 'r') as f:
- old_pid = f.read()
-
- if check_pid(int(old_pid)):
- logging.warning('Another process of nightly.py is still running,
quitting this one')
- quit()
-
-create_pid_file()
-
-
-def fetch_all_extensions():
- """
- Returns raw text of extension list,
- should not be called directly
- """
- logging.debug('Fetching list of all extensions...')
- data = {
- 'action': 'query',
- 'list': 'extdistrepos',
- 'format': 'json'
- }
- req = urllib.urlopen(conf.API_URL, urllib.urlencode(data))
- j = json.loads(req.read())
- req.close()
- return '\n'.join(j['query']['extdistrepos']['extensions'])
-
-
-def get_all_extensions(update=False):
- """
- Returns a list of all extension names,
- possibly using a cached list locally
- """
- fname = os.path.join(conf.EXT_PATH, 'extension-list')
- if update or not os.path.exists(fname):
- with open(fname, 'w') as f:
- exts = fetch_all_extensions()
- f.write(exts)
- else:
- logging.debug('Loading extension-list from file cache')
- with open(fname, 'r') as f:
- exts = f.read()
-
- return exts.strip().splitlines()
-
-
-def get_supported_branches():
- if conf.get('SUPPORTED_VERSIONS') is None:
- conf.SUPPORTED_VERSIONS = get_extension_config()['versions']
- return conf.SUPPORTED_VERSIONS
-
-
-def fetch_extension_config():
- logging.debug('Fetching ExtensionDistributor config from API...')
- data = {
- 'action': 'query',
- 'meta': 'siteinfo',
- 'format': 'json',
- }
- req = urllib.urlopen(conf.API_URL, urllib.urlencode(data))
- resp = json.loads(req.read())
- req.close()
- return {
- 'versions':
resp['query']['general']['extensiondistributor']['snapshots'],
- 'extension-list':
resp['query']['general']['extensiondistributor']['list']
- }
-
-
-def get_extension_config(update=False):
- fname = os.path.join(conf.EXT_PATH, 'mw-conf.json')
- if update or not os.path.exists(fname):
- with open(fname, 'w') as f:
- e_config = fetch_extension_config()
- json.dump(e_config, f)
- else:
- with open(fname, 'r') as f:
- try:
- e_config = json.load(f)
- except ValueError:
- e_config = None
-
- if e_config is None:
- e_config = get_extension_config(update=True)
-
- return e_config
-
-
-def shell_exec(args, **kwargs):
- """
- Shortcut wrapper to execute a shell command
-
- >>> shell_exec(['ls', '-l'])
- """
- return subprocess.check_output(args, **kwargs)
-
-
-def update_extension(ext):
- """
- Fetch an extension's updates, and
- create new tarballs if needed
- """
- full_path = os.path.join(conf.EXT_PATH, ext)
- logging.info('Starting update for %s' % ext)
- if not os.path.exists(full_path):
- os.chdir(conf.EXT_PATH)
- logging.debug('Cloning %s' % ext)
- shell_exec(['git', 'clone', conf.GIT_URL % ext, ext])
+class TarballGenerator(object):
+ def __init__(self, conf):
+ self.API_URL = conf['API_URL']
+ self.DIST_PATH = conf['DIST_PATH']
+ self.GIT_URL = conf['GIT_URL']
+ self.LOG_FILE = conf['LOG_FILE']
+ self.SRC_PATH = conf['SRC_PATH']
+ self.PID_FILE = conf['PID_FILE']
+ self.LOG_FILE = conf['LOG_FILE']
+ self.EXT_PATH = os.path.join(self.SRC_PATH, 'extensions')
+ self._extension_list = None
+ self._extension_config = None
pass
- for branch in get_supported_branches():
- os.chdir(full_path)
- logging.info('Creating %s for %s' % (branch, ext))
- # Update remotes
- shell_exec(['git', 'fetch'])
+
+ @property
+ def extension_list(self):
+ """
+ Lazy-load the list of all extensions
+ """
+ if self._extension_list is None:
+ self._extension_list = self.fetch_all_extensions()
+ return self._extension_list
+
+ def fetch_all_extensions(self):
+ """
+ Does an API request to get the complete list of extensions.
+ Do not call directly.
+ """
+ logging.debug('Fetching list of all extensions...')
+ data = {
+ 'action': 'query',
+ 'list': 'extdistrepos',
+ 'format': 'json'
+ }
+ req = urllib.urlopen(self.API_URL, urllib.urlencode(data))
+ j = json.loads(req.read())
+ req.close()
+ return j['query']['extdistrepos']['extensions']
+
+ @property
+ def supported_versions(self):
+ """
+ Lazy-load the list of supported branches
+ """
+ if self._extension_config is None:
+ self.fetch_extension_config()
+ return self._extension_config['snapshots']
+
+ def fetch_extension_config(self):
+ """
+ Fetch the ExtensionDistributor configuration from the API
+ Do not call this directly.
+ """
+ logging.debug('Fetching ExtensionDistributor config from API...')
+ data = {
+ 'action': 'query',
+ 'meta': 'siteinfo',
+ 'format': 'json',
+ }
+ req = urllib.urlopen(self.API_URL, urllib.urlencode(data))
+ resp = json.loads(req.read())
+ req.close()
+ self._extension_config =
resp['query']['general']['extensiondistributor']
+
+ return {
+ 'versions':
resp['query']['general']['extensiondistributor']['snapshots'],
+ 'extension-list':
resp['query']['general']['extensiondistributor']['list']
+ }
+
+ def init(self):
+ """
+ Does basic initialization
+ """
+ # Set up logging
+ logging.basicConfig(
+ filename=self.LOG_FILE,
+ level=logging.DEBUG,
+ format='%(asctime)s %(levelname)s:%(message)s'
+ )
+
+ # Check to make sure nightly.py isn't already running
+ if os.path.exists(self.PID_FILE):
+ with open(self.PID_FILE, 'r') as f:
+ old_pid = f.read()
+
+ if self.check_pid(int(old_pid)):
+ logging.warning('Another process of nightly.py is still
running, quitting this one')
+ quit()
+
+ self.create_pid_file()
+
+ # Init some directories we'll need
+ if not os.path.isdir(self.EXT_PATH):
+ self.shell_exec(['mkdir', '-p', self.EXT_PATH])
+ if not os.path.isdir(self.DIST_PATH):
+ self.shell_exec(['mkdir', '-p', self.DIST_PATH])
+
+ def shell_exec(self, args, **kwargs):
+ """
+ Shortcut wrapper to execute a shell command
+
+ >>> self.shell_exec(['ls', '-l'])
+ """
+ return subprocess.check_output(args, **kwargs)
+
+ def update_extension(self, ext):
+ """
+ Fetch an extension's updates, and
+ create new tarballs if needed
+ """
+ full_path = os.path.join(self.EXT_PATH, ext)
+ logging.info('Starting update for %s' % ext)
+ if not os.path.exists(full_path):
+ os.chdir(self.EXT_PATH)
+ logging.debug('Cloning %s' % ext)
+ self.shell_exec(['git', 'clone', self.GIT_URL % ext, ext])
+ pass
+ for branch in self.supported_versions:
+ os.chdir(full_path)
+ logging.info('Creating %s for %s' % (branch, ext))
+ # Update remotes
+ self.shell_exec(['git', 'fetch'])
+ try:
+ # Could fail if repo is empty
+ self.shell_exec(['git', 'reset', '--hard', 'origin/master'])
+ # Checkout the branch
+ self.shell_exec(['git', 'checkout', 'origin/%s' % branch])
+ except subprocess.CalledProcessError:
+ # Just a warning because this is expected for some extensions
+ logging.warning('could not checkout origin/%s' % branch)
+ continue
+ # Sync submodules in case their urls have changed
+ self.shell_exec(['git', 'submodule', 'sync'])
+ # Update them, initializing new ones if needed
+ self.shell_exec(['git', 'submodule', 'update', '--init'])
+ # Gets short hash of HEAD
+ rev = self.shell_exec(['git', 'rev-parse', '--short',
'HEAD']).strip()
+ tarball_fname = '%s-%s-%s.tar.gz' % (ext, branch, rev)
+ if os.path.exists(os.path.join(self.DIST_PATH, tarball_fname)):
+ logging.debug('No updates to branch, tarball already exists.')
+ continue
+ # Create a 'version' file with basic info about the tarball
+ with open('version', 'w') as f:
+ f.write('%s: %s\n' % (ext, branch))
+ f.write(self.shell_exec(['date', '+%Y-%m-%dT%H:%M:%S']) +
'\n') # TODO: Do this in python
+ f.write(rev + '\n')
+ old_tarballs = glob.glob(os.path.join(self.DIST_PATH,
'%s-%s-*.tar.gz' % (ext, branch)))
+ logging.debug('Deleting old tarballs...')
+ for old in old_tarballs:
+ # FIXME: Race condition, we should probably do this later on...
+ os.unlink(old)
+ os.chdir(self.EXT_PATH)
+ # Finally, create the new tarball
+ self.shell_exec(['tar', 'czPf', tarball_fname, ext])
+ logging.debug('Moving new tarballs into dist/')
+ tarballs = glob.glob(os.path.join(self.EXT_PATH, '*.tar.gz'))
+ for tar in tarballs:
+ fname = tar.split('/')[-1]
+ os.rename(tar, os.path.join(self.DIST_PATH, fname))
+ logging.info('Finished update for %s' % ext)
+
+ def check_pid(self, pid):
+ """
+ Checks whether the given pid is running
+ """
try:
- # Could fail if repo is empty
- shell_exec(['git', 'reset', '--hard', 'origin/master'])
- # Checkout the branch
- shell_exec(['git', 'checkout', 'origin/%s' % branch])
- except subprocess.CalledProcessError:
- # Just a warning because this is expected for some extensions
- logging.warning('could not checkout origin/%s' % branch)
- continue
- # Sync submodules in case their urls have changed
- shell_exec(['git', 'submodule', 'sync'])
- # Update them, initializing new ones if needed
- shell_exec(['git', 'submodule', 'update', '--init'])
- # Gets short hash of HEAD
- rev = shell_exec(['git', 'rev-parse', '--short', 'HEAD']).strip()
- tarball_fname = '%s-%s-%s.tar.gz' % (ext, branch, rev)
- if os.path.exists(os.path.join(conf.DIST_PATH, tarball_fname)):
- logging.debug('No updates to branch, tarball already exists.')
- continue
- # Create a 'version' file with basic info about the tarball
- with open('version', 'w') as f:
- f.write('%s: %s\n' % (ext, branch))
- f.write(shell_exec(['date', '+%Y-%m-%dT%H:%M:%S']) + '\n') #
TODO: Do this in python
- f.write(rev + '\n')
- old_tarballs = glob.glob(os.path.join(conf.DIST_PATH, '%s-%s-*.tar.gz'
% (ext, branch)))
- logging.debug('Deleting old tarballs...')
- for old in old_tarballs:
- # FIXME: Race condition, we should probably do this later on...
- os.unlink(old)
- os.chdir(conf.EXT_PATH)
- # Finally, create the new tarball
- shell_exec(['tar', 'czPf', tarball_fname, ext])
- logging.debug('Moving new tarballs into dist/')
- tarballs = glob.glob(os.path.join(conf.EXT_PATH, '*.tar.gz'))
- for tar in tarballs:
- fname = tar.split('/')[-1]
- os.rename(tar, os.path.join(conf.DIST_PATH, fname))
- logging.info('Finished update for %s' % ext)
+ # This doesn't actually kill it, just checks if it is running
+ os.kill(pid, 0)
+ except OSError:
+ # Not running
+ return False
+ else:
+ # So it must be running
+ return True
+
+ def create_pid_file(self):
+ """
+ Creates a pid file with the current pid
+ """
+ with open(self.PID_FILE, 'w') as f:
+ f.write(str(os.getpid()))
+ logging.info('Creating pid file')
+
+ def run(self):
+ self.init()
+ extensions = self.extension_list
+ logging.info('Processing %s extensions' % len(extensions))
+ logging.info('Starting update of all extensions...')
+ for ext in extensions:
+ try:
+ self.update_extension(ext)
+ except:
+ logging.error('Updating %s failed, skipping' % ext)
+ logging.info('Finished update of all extensions!')
def main():
- """
- Updates all extensions
- """
- # Update MW config
- get_extension_config(update=True)
- extensions = get_all_extensions(update=True)
- logging.info('Processing %s extensions' % len(extensions))
- logging.info('Starting update of all extensions...')
- for ext in extensions:
- try:
- update_extension(ext)
- except:
- logging.error('Updating %s failed, skipping' % ext)
- logging.info('Finished update of all extensions!')
-
-
-# Init
-if not os.path.isdir(conf.EXT_PATH):
- shell_exec(['mkdir', '-p', conf.EXT_PATH])
-if not os.path.isdir(conf.DIST_PATH):
- shell_exec(['mkdir', '-p', conf.DIST_PATH])
+ # Load our config from JSON
+ conf = None
+ if os.path.exists('/etc/extdist.conf'):
+ with open('/etc/extdist.conf', 'r') as f:
+ conf = json.load(f)
+ elif os.path.exists(os.path.join(os.path.dirname(__file__), 'conf.json')):
+ with open(os.path.join(os.path.dirname(__file__), 'conf.json'), 'r')
as f:
+ conf = json.load(f)
+ else:
+ print 'extdist is not configured properly.'
+ quit()
+ generator = TarballGenerator(conf)
+ generator.run()
if __name__ == '__main__':
- if '--all' in sys.argv:
- main()
- else:
- update_extension('VisualEditor')
+ main()
--
To view, visit https://gerrit.wikimedia.org/r/169309
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: If1d283b02ee72fae1709bba80755a342e684c320
Gerrit-PatchSet: 1
Gerrit-Project: labs/tools/extdist
Gerrit-Branch: master
Gerrit-Owner: Legoktm <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits