chiark / gitweb /
build/checkupdates/update: log current fdroiddata commit to wiki
[fdroidserver.git] / fdroidserver / checkupdates.py
index 02baa061d54f085471a188a5a17bda95cc8585df..72c8b22b4fa3407542e9fc55404e3a0985ffe530 100644 (file)
@@ -23,17 +23,19 @@ import urllib.request
 import urllib.error
 import time
 import subprocess
+import sys
 from argparse import ArgumentParser
 import traceback
 import html
 from distutils.version import LooseVersion
 import logging
 import copy
+import urllib.parse
 
 from . import _
 from . import common
 from . import metadata
-from .exception import VCSException, FDroidException, MetaDataException
+from .exception import VCSException, NoSubmodulesException, FDroidException, MetaDataException
 
 
 # Check for a new version by looking at a document retrieved via HTTP.
@@ -47,6 +49,13 @@ def check_http(app):
             raise FDroidException('Missing Update Check Data')
 
         urlcode, codeex, urlver, verex = app.UpdateCheckData.split('|')
+        parsed = urllib.parse.urlparse(urlcode)
+        if not parsed.netloc or not parsed.scheme or parsed.scheme != 'https':
+            raise FDroidException(_('UpdateCheckData has invalid URL: {url}').format(url=urlcode))
+        if urlver != '.':
+            parsed = urllib.parse.urlparse(urlver)
+            if not parsed.netloc or not parsed.scheme or parsed.scheme != 'https':
+                raise FDroidException(_('UpdateCheckData has invalid URL: {url}').format(url=urlcode))
 
         vercode = "99999999"
         if len(urlcode) > 0:
@@ -110,8 +119,7 @@ def check_tags(app, pattern):
 
         last_build = app.get_last_build()
 
-        if last_build.submodules:
-            vcs.initsubmodules()
+        try_init_submodules(app, last_build, vcs)
 
         hpak = None
         htag = None
@@ -207,8 +215,7 @@ def check_repomanifest(app, branch=None):
         if len(app.builds) > 0:
             last_build = app.builds[-1]
 
-        if last_build.submodules:
-            vcs.initsubmodules()
+        try_init_submodules(app, last_build, vcs)
 
         hpak = None
         hver = None
@@ -300,6 +307,19 @@ def check_gplay(app):
     return (version.strip(), None)
 
 
+def try_init_submodules(app, last_build, vcs):
+    """Try to init submodules if the last build entry used them.
+    They might have been removed from the app's repo in the meantime,
+    so if we can't find any submodules we continue with the updates check.
+    If there is any other error in initializing them then we stop the check.
+    """
+    if last_build.submodules:
+        try:
+            vcs.initsubmodules()
+        except NoSubmodulesException:
+            logging.info("No submodules present for {}".format(app.Name))
+
+
 # Return all directories under startdir that contain any of the manifest
 # files, and thus are probably an Android project.
 def dirs_with_manifest(startdir):
@@ -409,6 +429,9 @@ def checkupdates_app(app):
         msg = 'Invalid update check method'
 
     if version and vercode and app.VercodeOperation:
+        if not common.VERCODE_OPERATION_RE.match(app.VercodeOperation):
+            raise MetaDataException(_('Invalid VercodeOperation: {field}')
+                                    .format(field=app.VercodeOperation))
         oldvercode = str(int(vercode))
         op = app.VercodeOperation.replace("%c", oldvercode)
         vercode = str(eval(op))
@@ -499,8 +522,41 @@ def checkupdates_app(app):
                 raise FDroidException("Git commit failed")
 
 
+def update_wiki(gplaylog, locallog):
+    if config.get('wiki_server') and config.get('wiki_path'):
+        try:
+            import mwclient
+            site = mwclient.Site((config['wiki_protocol'], config['wiki_server']),
+                                 path=config['wiki_path'])
+            site.login(config['wiki_user'], config['wiki_password'])
+
+            # Write a page with the last build log for this version code
+            wiki_page_path = 'checkupdates_' + time.strftime('%s', start_timestamp)
+            newpage = site.Pages[wiki_page_path]
+            txt = ''
+            txt += "* command line: <code>" + ' '.join(sys.argv) + "</code>\n"
+            txt += common.get_git_describe_link()
+            txt += "* started at " + common.get_wiki_timestamp(start_timestamp) + '\n'
+            txt += "* completed at " + common.get_wiki_timestamp() + '\n'
+            txt += "\n\n"
+            txt += common.get_android_tools_version_log()
+            txt += "\n\n"
+            if gplaylog:
+                txt += '== --gplay check ==\n\n'
+                txt += gplaylog
+            if locallog:
+                txt += '== local source check ==\n\n'
+                txt += locallog
+            newpage.save(txt, summary='Run log')
+            newpage = site.Pages['checkupdates']
+            newpage.save('#REDIRECT [[' + wiki_page_path + ']]', summary='Update redirect')
+        except Exception as e:
+            logging.error(_('Error while attempting to publish log: %s') % e)
+
+
 config = None
 options = None
+start_timestamp = time.gmtime()
 
 
 def main():
@@ -517,6 +573,8 @@ def main():
                         help=_("Only process apps with auto-updates"))
     parser.add_argument("--commit", action="store_true", default=False,
                         help=_("Commit changes"))
+    parser.add_argument("--allow-dirty", action="store_true", default=False,
+                        help=_("Run on git repo that has uncommitted changes"))
     parser.add_argument("--gplay", action="store_true", default=False,
                         help=_("Only print differences with the Play Store"))
     metadata.add_metadata_arguments(parser)
@@ -525,13 +583,21 @@ def main():
 
     config = common.read_config(options)
 
+    if not options.allow_dirty:
+        status = subprocess.check_output(['git', 'status', '--porcelain'])
+        if status:
+            logging.error(_('Build metadata git repo has uncommited changes!'))
+            sys.exit(1)
+
     # Get all apps...
     allapps = metadata.read_metadata()
 
     apps = common.read_app_args(options.appid, allapps, False)
 
+    gplaylog = ''
     if options.gplay:
         for appid, app in apps.items():
+            gplaylog += '* ' + appid + '\n'
             version, reason = check_gplay(app)
             if version is None:
                 if reason == '404':
@@ -553,21 +619,28 @@ def main():
                     else:
                         logging.info("{0} has the same version {1} on the Play Store"
                                      .format(common.getappname(app), version))
+        update_wiki(gplaylog, None)
         return
 
+    locallog = ''
     for appid, app in apps.items():
 
         if options.autoonly and app.AutoUpdateMode in ('None', 'Static'):
             logging.debug(_("Nothing to do for {appid}.").format(appid=appid))
             continue
 
-        logging.info(_("Processing {appid}").format(appid=appid))
+        msg = _("Processing {appid}").format(appid=appid)
+        logging.info(msg)
+        locallog += '* ' + msg + '\n'
 
         try:
             checkupdates_app(app)
         except Exception as e:
-            logging.error(_("...checkupdate failed for {appid} : {error}")
-                          .format(appid=appid, error=e))
+            msg = _("...checkupdate failed for {appid} : {error}").format(appid=appid, error=e)
+            logging.error(msg)
+            locallog += msg + '\n'
+
+    update_wiki(None, locallog)
 
     logging.info(_("Finished"))