chiark / gitweb /
Merge branch 'implement-gettext' into 'master'
authorHans-Christoph Steiner <hans@guardianproject.info>
Fri, 15 Sep 2017 19:29:12 +0000 (19:29 +0000)
committerHans-Christoph Steiner <hans@guardianproject.info>
Fri, 15 Sep 2017 19:29:12 +0000 (19:29 +0000)
first implementation of localization using gettext

Closes #342

See merge request fdroid/fdroidserver!338

27 files changed:
.gitignore
.gitlab-ci.yml
fdroid
fdroidserver/__init__.py
fdroidserver/btlog.py
fdroidserver/build.py
fdroidserver/checkupdates.py
fdroidserver/common.py
fdroidserver/dscanner.py
fdroidserver/gpgsign.py
fdroidserver/import.py
fdroidserver/index.py
fdroidserver/init.py
fdroidserver/install.py
fdroidserver/lint.py
fdroidserver/metadata.py
fdroidserver/publish.py
fdroidserver/rewritemeta.py
fdroidserver/scanner.py
fdroidserver/server.py
fdroidserver/signatures.py
fdroidserver/signindex.py
fdroidserver/stats.py
fdroidserver/update.py
fdroidserver/verify.py
setup.py
tests/update.TestCase

index a606026bc046d357d797730588a937782e9b46b0..3bccb197d772d4f9b9f143e8be885fb5ed08cbb2 100644 (file)
@@ -44,3 +44,6 @@ makebuildserver.config.py
 /tests/repo/info.guardianproject.checkey/en-US/phoneScreenshots/checkey.png
 /tests/urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234.apk
 /unsigned/
+
+# generated by gettext
+locale/*/LC_MESSAGES/fdroidserver.mo
index 6ce9064e37f70e86ddc45ab9d6faa9b47d7534d3..0b0a4d3e5e4562170ee85d4d3b5e4fdace523513 100644 (file)
@@ -6,10 +6,12 @@ test:
     - cd tests
     - ./complete-ci-tests
 
-# Test that the parsing of the .txt format didn't change 
-# from last released version.
+# Test that the parsing of the .txt format didn't change from last
+# released version. Ensure that the official tags are included when
+# running these tests on forks as well.
 metadata_v0:
   script:
+    - git fetch https://gitlab.com/fdroid/fdroidserver 0.8
     - cd tests
     - export GITCOMMIT=`git describe`
     - git checkout 0.8  # bump after release
diff --git a/fdroid b/fdroid
index 0b483e0150e98f795e39f84d7096e83c4e9e5159..467551c00989c6007f52466b98f2ca735bf58a80 100755 (executable)
--- a/fdroid
+++ b/fdroid
@@ -22,36 +22,38 @@ import logging
 
 import fdroidserver.common
 import fdroidserver.metadata
+from fdroidserver import _
 from argparse import ArgumentError
 from collections import OrderedDict
 
+
 commands = OrderedDict([
-    ("build", "Build a package from source"),
-    ("init", "Quickly start a new repository"),
-    ("publish", "Sign and place packages in the repo"),
-    ("gpgsign", "Add gpg signatures for packages in repo"),
-    ("update", "Update repo information for new packages"),
-    ("verify", "Verify the integrity of downloaded packages"),
-    ("checkupdates", "Check for updates to applications"),
-    ("import", "Add a new application from its source code"),
-    ("install", "Install built packages on devices"),
-    ("readmeta", "Read all the metadata files and exit"),
-    ("rewritemeta", "Rewrite all the metadata files"),
-    ("lint", "Warn about possible metadata errors"),
-    ("scanner", "Scan the source code of a package"),
-    ("dscanner", "Dynamically scan APKs post build"),
-    ("stats", "Update the stats of the repo"),
-    ("server", "Interact with the repo HTTP server"),
-    ("signindex", "Sign indexes created using update --nosign"),
-    ("btlog", "Update the binary transparency log for a URL"),
-    ("signatures", "Extract signatures from APKs"),
+    ("build", _("Build a package from source")),
+    ("init", _("Quickly start a new repository")),
+    ("publish", _("Sign and place packages in the repo")),
+    ("gpgsign", _("Add gpg signatures for packages in repo")),
+    ("update", _("Update repo information for new packages")),
+    ("verify", _("Verify the integrity of downloaded packages")),
+    ("checkupdates", _("Check for updates to applications")),
+    ("import", _("Add a new application from its source code")),
+    ("install", _("Install built packages on devices")),
+    ("readmeta", _("Read all the metadata files and exit")),
+    ("rewritemeta", _("Rewrite all the metadata files")),
+    ("lint", _("Warn about possible metadata errors")),
+    ("scanner", _("Scan the source code of a package")),
+    ("dscanner", _("Dynamically scan APKs post build")),
+    ("stats", _("Update the stats of the repo")),
+    ("server", _("Interact with the repo HTTP server")),
+    ("signindex", _("Sign indexes created using update --nosign")),
+    ("btlog", _("Update the binary transparency log for a URL")),
+    ("signatures", _("Extract signatures from APKs")),
 ])
 
 
 def print_help():
-    print("usage: fdroid [-h|--help|--version] <command> [<args>]")
+    print(_("usage: fdroid [-h|--help|--version] <command> [<args>]"))
     print("")
-    print("Valid commands are:")
+    print(_("Valid commands are:"))
     for cmd, summary in commands.items():
         print("   " + cmd + ' ' * (15 - len(cmd)) + summary)
     print("")
@@ -70,7 +72,7 @@ def main():
             sys.exit(0)
         elif command == '--version':
             import os.path
-            output = 'no version info found!'
+            output = _('no version info found!')
             cmddir = os.path.realpath(os.path.dirname(__file__))
             moduledir = os.path.realpath(os.path.dirname(fdroidserver.common.__file__) + '/..')
             if cmddir == moduledir:
@@ -97,7 +99,7 @@ def main():
             print(output),
             sys.exit(0)
         else:
-            print("Command '%s' not recognised.\n" % command)
+            print(_("Command '%s' not recognised.\n" % command))
             print_help()
             sys.exit(1)
 
@@ -143,7 +145,7 @@ def main():
     # These should only be unexpected crashes due to bugs in the code
     # str(e) often doesn't contain a reason, so just show the backtrace
     except Exception as e:
-        logging.critical("Unknown exception found!")
+        logging.critical(_("Unknown exception found!"))
         raise
     sys.exit(0)
 
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2e88551a8f7ad9d048b84f62c4f938e345ce8236 100644 (file)
@@ -0,0 +1,22 @@
+
+import gettext
+import glob
+import os
+import sys
+
+
+# support running straight from git and standard installs
+rootpaths = [
+    os.path.realpath(os.path.join(os.path.dirname(__file__), '..')),
+    sys.prefix + 'share',
+]
+
+localedir = None
+for rootpath in rootpaths:
+    if len(glob.glob(os.path.join(rootpath, 'locale', '*', 'LC_MESSAGES', 'fdroidserver.mo'))) > 0:
+        localedir = os.path.join(rootpath, 'locale')
+        break
+
+gettext.bindtextdomain('fdroidserver', localedir)
+gettext.textdomain('fdroidserver')
+_ = gettext.gettext
index 452addca1dcc9d84d45e4bfae2356a7e9623a1f5..de357039758a5e7c369610b7d5fe9735fa9bac53 100755 (executable)
@@ -40,9 +40,10 @@ import xml.dom.minidom
 import zipfile
 from argparse import ArgumentParser
 
-from .exception import FDroidException
+from . import _
 from . import common
 from . import server
+from .exception import FDroidException
 
 
 options = None
@@ -117,12 +118,12 @@ For more info on this idea:
             jarin.close()
             gitrepo.index.add([repof, ])
 
-        files = []
-        for root, dirs, filenames in os.walk(repodir):
-            for f in filenames:
-                files.append(os.path.relpath(os.path.join(root, f), repodir))
+        output_files = []
+        for root, dirs, files in os.walk(repodir):
+            for f in files:
+                output_files.append(os.path.relpath(os.path.join(root, f), repodir))
         output = collections.OrderedDict()
-        for f in sorted(files):
+        for f in sorted(output_files):
             repofile = os.path.join(repodir, f)
             stat = os.stat(repofile)
             output[f] = (
@@ -151,11 +152,11 @@ def main():
     common.setup_global_opts(parser)
     parser.add_argument("--git-repo",
                         default=os.path.join(os.getcwd(), 'binary_transparency'),
-                        help="Path to the git repo to use as the log")
+                        help=_("Path to the git repo to use as the log"))
     parser.add_argument("-u", "--url", default='https://f-droid.org',
-                        help="The base URL for the repo to log (default: https://f-droid.org)")
+                        help=_("The base URL for the repo to log (default: https://f-droid.org)"))
     parser.add_argument("--git-remote", default=None,
-                        help="Push the log to this git remote repository")
+                        help=_("Push the log to this git remote repository"))
     options = parser.parse_args()
 
     if options.verbose:
index 5dfe63ba747085f2bba260e4b3daa732ed528920..889945d320720bd9776d2663fc392d2bf7769a2f 100644 (file)
@@ -32,6 +32,7 @@ from configparser import ConfigParser
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from . import net
 from . import metadata
@@ -96,19 +97,19 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force):
 
         # Helper to copy the contents of a directory to the server...
         def send_dir(path):
-            root = os.path.dirname(path)
+            startroot = os.path.dirname(path)
             main = os.path.basename(path)
             ftp.mkdir(main)
-            for r, d, f in os.walk(path):
-                rr = os.path.relpath(rroot)
+            for root, dirs, files in os.walk(path):
+                rr = os.path.relpath(root, startroot)
                 ftp.chdir(rr)
-                for dd in d:
-                    ftp.mkdir(dd)
-                for ff in f:
-                    lfile = os.path.join(root, rr, ff)
+                for d in dirs:
+                    ftp.mkdir(d)
+                for f in files:
+                    lfile = os.path.join(startroot, rr, f)
                     if not os.path.islink(lfile):
-                        ftp.put(lfile, ff)
-                        ftp.chmod(ff, os.stat(lfile).st_mode)
+                        ftp.put(lfile, f)
+                        ftp.chmod(f, os.stat(lfile).st_mode)
                 for i in range(len(rr.split('/'))):
                     ftp.chdir('..')
             ftp.chdir('..')
@@ -162,7 +163,7 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force):
                         ftp.mkdir(d)
                     ftp.chdir(d)
                 ftp.put(libsrc, lp[-1])
-                for _ in lp[:-1]:
+                for _ignored in lp[:-1]:
                     ftp.chdir('..')
         # Copy any srclibs that are required...
         srclibpaths = []
@@ -995,33 +996,33 @@ def parse_commandline():
 
     parser = ArgumentParser(usage="%(prog)s [options] [APPID[:VERCODE] [APPID[:VERCODE] ...]]")
     common.setup_global_opts(parser)
-    parser.add_argument("appid", nargs='*', help="app-id with optional versionCode in the form APPID[:VERCODE]")
+    parser.add_argument("appid", nargs='*', help=_("applicationId with optional versionCode in the form APPID[:VERCODE]"))
     parser.add_argument("-l", "--latest", action="store_true", default=False,
-                        help="Build only the latest version of each package")
+                        help=_("Build only the latest version of each package"))
     parser.add_argument("-s", "--stop", action="store_true", default=False,
-                        help="Make the build stop on exceptions")
+                        help=_("Make the build stop on exceptions"))
     parser.add_argument("-t", "--test", action="store_true", default=False,
-                        help="Test mode - put output in the tmp directory only, and always build, even if the output already exists.")
+                        help=_("Test mode - put output in the tmp directory only, and always build, even if the output already exists."))
     parser.add_argument("--server", action="store_true", default=False,
-                        help="Use build server")
+                        help=_("Use build server"))
     parser.add_argument("--resetserver", action="store_true", default=False,
-                        help="Reset and create a brand new build server, even if the existing one appears to be ok.")
+                        help=_("Reset and create a brand new build server, even if the existing one appears to be ok."))
     parser.add_argument("--on-server", dest="onserver", action="store_true", default=False,
-                        help="Specify that we're running on the build server")
+                        help=_("Specify that we're running on the build server"))
     parser.add_argument("--skip-scan", dest="skipscan", action="store_true", default=False,
-                        help="Skip scanning the source code for binaries and other problems")
+                        help=_("Skip scanning the source code for binaries and other problems"))
     parser.add_argument("--dscanner", action="store_true", default=False,
-                        help="Setup an emulator, install the apk on it and perform a drozer scan")
+                        help=_("Setup an emulator, install the apk on it and perform a drozer scan"))
     parser.add_argument("--no-tarball", dest="notarball", action="store_true", default=False,
-                        help="Don't create a source tarball, useful when testing a build")
+                        help=_("Don't create a source tarball, useful when testing a build"))
     parser.add_argument("--no-refresh", dest="refresh", action="store_false", default=True,
-                        help="Don't refresh the repository, useful when testing a build with no internet connection")
+                        help=_("Don't refresh the repository, useful when testing a build with no internet connection"))
     parser.add_argument("-f", "--force", action="store_true", default=False,
-                        help="Force build of disabled apps, and carries on regardless of scan problems. Only allowed in test mode.")
+                        help=_("Force build of disabled apps, and carries on regardless of scan problems. Only allowed in test mode."))
     parser.add_argument("-a", "--all", action="store_true", default=False,
-                        help="Build all applications available")
+                        help=_("Build all applications available"))
     parser.add_argument("-w", "--wiki", default=False, action="store_true",
-                        help="Update the wiki")
+                        help=_("Update the wiki"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -1316,7 +1317,7 @@ def main():
         logging.info("Cleaning up after ourselves.")
         docker.clean()
 
-    logging.info("Finished.")
+    logging.info(_("Finished"))
     if len(build_succeeded) > 0:
         logging.info(str(len(build_succeeded)) + ' builds succeeded')
     if len(failed_apps) > 0:
index 217139d19a956fb052d5ac5f5365e89615ba5f49..7c269c1f366f849531e4c3b4bbd94555ec6ac19b 100644 (file)
@@ -30,6 +30,7 @@ from distutils.version import LooseVersion
 import logging
 import copy
 
+from . import _
 from . import common
 from . import metadata
 from .exception import VCSException, FDroidException, MetaDataException
@@ -302,10 +303,10 @@ def check_gplay(app):
 # Return all directories under startdir that contain any of the manifest
 # files, and thus are probably an Android project.
 def dirs_with_manifest(startdir):
-    for r, d, f in os.walk(startdir):
-        if any(m in f for m in [
+    for root, dirs, files in os.walk(startdir):
+        if any(m in files for m in [
                 'AndroidManifest.xml', 'pom.xml', 'build.gradle']):
-            yield r
+            yield root
 
 
 # Tries to find a new subdir starting from the root build_dir. Returns said
@@ -509,15 +510,15 @@ def main():
     # Parse command line...
     parser = ArgumentParser(usage="%(prog)s [options] [APPID [APPID ...]]")
     common.setup_global_opts(parser)
-    parser.add_argument("appid", nargs='*', help="app-id to check for updates")
+    parser.add_argument("appid", nargs='*', help=_("applicationId to check for updates"))
     parser.add_argument("--auto", action="store_true", default=False,
-                        help="Process auto-updates")
+                        help=_("Process auto-updates"))
     parser.add_argument("--autoonly", action="store_true", default=False,
-                        help="Only process apps with auto-updates")
+                        help=_("Only process apps with auto-updates"))
     parser.add_argument("--commit", action="store_true", default=False,
-                        help="Commit changes")
+                        help=_("Commit changes"))
     parser.add_argument("--gplay", action="store_true", default=False,
-                        help="Only print differences with the Play Store")
+                        help=_("Only print differences with the Play Store"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -567,7 +568,7 @@ def main():
         except Exception as e:
             logging.error("...checkupdate failed for {0} : {1}".format(appid, e))
 
-    logging.info("Finished.")
+    logging.info(_("Finished"))
 
 
 if __name__ == "__main__":
index 182949d928ecc8162d29b6e0b385f0b7f451ab97..815cf3eb7fd4f04af511fe4ee2ac9648e2eaa090 100644 (file)
@@ -49,6 +49,7 @@ from pyasn1.error import PyAsn1Error
 from distutils.util import strtobool
 
 import fdroidserver.metadata
+from fdroidserver import _
 from fdroidserver.exception import FDroidException, VCSException, BuildException
 from .asynchronousfilereader import AsynchronousFileReader
 
@@ -123,9 +124,9 @@ default_config = {
 
 def setup_global_opts(parser):
     parser.add_argument("-v", "--verbose", action="store_true", default=False,
-                        help="Spew out even more information than normal")
+                        help=_("Spew out even more information than normal"))
     parser.add_argument("-q", "--quiet", action="store_true", default=False,
-                        help="Restrict output to warnings and errors")
+                        help=_("Restrict output to warnings and errors"))
 
 
 def fill_config_defaults(thisconfig):
@@ -1022,9 +1023,9 @@ def retrieve_string(app_dir, string, xmlfiles=None):
             os.path.join(app_dir, 'res'),
             os.path.join(app_dir, 'src', 'main', 'res'),
         ]:
-            for r, d, f in os.walk(res_dir):
-                if os.path.basename(r) == 'values':
-                    xmlfiles += [os.path.join(r, x) for x in f if x.endswith('.xml')]
+            for root, dirs, files in os.walk(res_dir):
+                if os.path.basename(root) == 'values':
+                    xmlfiles += [os.path.join(root, x) for x in files if x.endswith('.xml')]
 
     name = string[len('@string/'):]
 
index a9ffa0275f7ffbe43072313489cb902fde6a9031..5622fc4dfc1b9fd9372c125eafaf5feb43f2a8ff 100644 (file)
@@ -24,7 +24,9 @@ from time import sleep
 from argparse import ArgumentParser
 from subprocess import CalledProcessError, check_output
 
-from fdroidserver import common, metadata
+from . import _
+from . import common
+from . import metadata
 
 try:
     from docker import Client
@@ -407,25 +409,25 @@ def main():
 
     parser.add_argument(
         "app_id", nargs='*',
-        help="app-id with optional versioncode in the form APPID[:VERCODE]")
+        help=_("applicationId with optional versionCode in the form APPID[:VERCODE]"))
     parser.add_argument(
         "-l", "--latest", action="store_true", default=False,
-        help="Scan only the latest version of each package")
+        help=_("Scan only the latest version of each package"))
     parser.add_argument(
         "--clean-after", default=False, action='store_true',
-        help="Clean after all scans have finished")
+        help=_("Clean after all scans have finished"))
     parser.add_argument(
         "--clean-before", default=False, action='store_true',
-        help="Clean before the scans start and rebuild the container")
+        help=_("Clean before the scans start and rebuild the container"))
     parser.add_argument(
         "--clean-only", default=False, action='store_true',
-        help="Clean up all containers and then exit")
+        help=_("Clean up all containers and then exit"))
     parser.add_argument(
         "--init-only", default=False, action='store_true',
-        help="Prepare drozer to run a scan")
+        help=_("Prepare drozer to run a scan"))
     parser.add_argument(
         "--repo-path", default="repo", action="store",
-        help="Override path for repo APKs (default: ./repo)")
+        help=_("Override path for repo APKs (default: ./repo)"))
 
     options = parser.parse_args()
     config = common.read_config(options)
index 159ddf408730a8811d07ccaaa16d41b0207fc83a..b942a21beb54940fb5147f4dae8cdcef141110af 100644 (file)
@@ -21,6 +21,7 @@ import glob
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from .common import FDroidPopen
 from .exception import FDroidException
@@ -46,7 +47,7 @@ def main():
 
     for output_dir in repodirs:
         if not os.path.isdir(output_dir):
-            raise FDroidException("Missing output directory '" + output_dir + "'")
+            raise FDroidException(_("Missing output directory") + " '" + output_dir + "'")
 
         # Process any apks that are waiting to be signed...
         for f in sorted(glob.glob(os.path.join(output_dir, '*.*'))):
index 43e395b74557da927c53168ec70c04f9d3d1af65..ba2f934215af14907616aebfbbe986cffd5522e6 100644 (file)
@@ -26,6 +26,7 @@ from argparse import ArgumentParser
 from configparser import ConfigParser
 import logging
 
+from . import _
 from . import common
 from . import metadata
 from .exception import FDroidException
@@ -59,7 +60,7 @@ def getrepofrompage(url):
         repo = page[index + 9:]
         index = repo.find('<')
         if index == -1:
-            return (None, "Error while getting repo address")
+            return (None, _("Error while getting repo address"))
         repo = repo[:index]
         repo = repo.split('"')[0]
         return (repotype, repo)
@@ -71,12 +72,12 @@ def getrepofrompage(url):
         repo = page[index + 10:]
         index = repo.find('<')
         if index == -1:
-            return (None, "Error while getting repo address")
+            return (None, _("Error while getting repo address"))
         repo = repo[:index]
         repo = repo.split('"')[0]
         return (repotype, repo)
 
-    return (None, "No information found." + page)
+    return (None, _("No information found.") + page)
 
 
 config = None
@@ -87,7 +88,7 @@ def get_metadata_from_url(app, url):
 
     tmp_dir = 'tmp'
     if not os.path.isdir(tmp_dir):
-        logging.info("Creating temporary directory")
+        logging.info(_("Creating temporary directory"))
         os.makedirs(tmp_dir)
 
     # Figure out what kind of project it is...
@@ -190,15 +191,15 @@ def main():
     parser = ArgumentParser()
     common.setup_global_opts(parser)
     parser.add_argument("-u", "--url", default=None,
-                        help="Project URL to import from.")
+                        help=_("Project URL to import from."))
     parser.add_argument("-s", "--subdir", default=None,
-                        help="Path to main android project subdirectory, if not in root.")
+                        help=_("Path to main android project subdirectory, if not in root."))
     parser.add_argument("-c", "--categories", default=None,
-                        help="Comma separated list of categories.")
+                        help=_("Comma separated list of categories."))
     parser.add_argument("-l", "--license", default=None,
-                        help="Overall license of the project.")
+                        help=_("Overall license of the project."))
     parser.add_argument("--rev", default=None,
-                        help="Allows a different revision (or git branch) to be specified for the initial import")
+                        help=_("Allows a different revision (or git branch) to be specified for the initial import"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -214,7 +215,7 @@ def main():
 
     local_metadata_files = common.get_local_metadata_files()
     if local_metadata_files != []:
-        raise FDroidException("This repo already has local metadata: %s" % local_metadata_files[0])
+        raise FDroidException(_("This repo already has local metadata: %s") % local_metadata_files[0])
 
     if options.url is None and os.path.isdir('.git'):
         app.AutoName = os.path.basename(os.getcwd())
@@ -252,11 +253,11 @@ def main():
 
         versionName, versionCode, package = common.parse_androidmanifests(paths, app)
         if not package:
-            raise FDroidException("Couldn't find package ID")
+            raise FDroidException(_("Couldn't find package ID"))
         if not versionName:
-            logging.warn("Couldn't find latest version name")
+            logging.warn(_("Couldn't find latest version name"))
         if not versionCode:
-            logging.warn("Couldn't find latest version code")
+            logging.warn(_("Couldn't find latest version code"))
     else:
         spec = os.path.join(root_dir, 'buildozer.spec')
         if os.path.exists(spec):
@@ -268,7 +269,7 @@ def main():
             versionName = bconfig.get('app', 'version')
             versionCode = None
         else:
-            raise FDroidException("No android or kivy project could be found. Specify --subdir?")
+            raise FDroidException(_("No android or kivy project could be found. Specify --subdir?"))
 
     # Make sure it's actually new...
     if package in apps:
index 53d2787aa58fbda5d350fcf0cd40f4032e6c9f72..59139e17f70b0492aaa903c141dac4590ebebd82 100644 (file)
@@ -34,7 +34,11 @@ from binascii import hexlify, unhexlify
 from datetime import datetime
 from xml.dom.minidom import Document
 
-from fdroidserver import metadata, signindex, common, net
+from . import _
+from . import common
+from . import metadata
+from . import net
+from . import signindex
 from fdroidserver.common import FDroidPopen, FDroidPopenBytes
 from fdroidserver.exception import FDroidException, VerificationException, MetaDataException
 
@@ -62,16 +66,16 @@ def make(apps, sortedids, apks, repodir, archive):
     if not common.options.nosign:
         if 'repo_keyalias' not in common.config:
             nosigningkey = True
-            logging.critical("'repo_keyalias' not found in config.py!")
+            logging.critical(_("'repo_keyalias' not found in config.py!"))
         if 'keystore' not in common.config:
             nosigningkey = True
-            logging.critical("'keystore' not found in config.py!")
+            logging.critical(_("'keystore' not found in config.py!"))
         if 'keystorepass' not in common.config:
             nosigningkey = True
-            logging.critical("'keystorepass' not found in config.py!")
+            logging.critical(_("'keystorepass' not found in config.py!"))
         if 'keypass' not in common.config:
             nosigningkey = True
-            logging.critical("'keypass' not found in config.py!")
+            logging.critical(_("'keypass' not found in config.py!"))
         if not os.path.exists(common.config['keystore']):
             nosigningkey = True
             logging.critical("'" + common.config['keystore'] + "' does not exist!")
@@ -104,7 +108,7 @@ def make(apps, sortedids, apks, repodir, archive):
     for mirror in sorted(common.config.get('mirrors', [])):
         base = os.path.basename(urllib.parse.urlparse(mirror).path.rstrip('/'))
         if common.config.get('nonstandardwebroot') is not True and base != 'fdroid':
-            logging.error("mirror '" + mirror + "' does not end with 'fdroid'!")
+            logging.error(_("mirror '%s' does not end with 'fdroid'!") % mirror)
             mirrorcheckfailed = True
         # must end with / or urljoin strips a whole path segment
         if mirror.endswith('/'):
@@ -115,7 +119,7 @@ def make(apps, sortedids, apks, repodir, archive):
         for url in get_mirror_service_urls(mirror):
             mirrors.append(url + '/' + repodir)
     if mirrorcheckfailed:
-        raise FDroidException("Malformed repository mirrors.")
+        raise FDroidException(_("Malformed repository mirrors."))
     if mirrors:
         repodict['mirrors'] = mirrors
 
@@ -144,7 +148,7 @@ def make(apps, sortedids, apks, repodir, archive):
             elif all(isinstance(item, str) for item in common.config[key]):
                 packageNames = common.config[key]
             else:
-                raise TypeError('only accepts strings, lists, and tuples')
+                raise TypeError(_('only accepts strings, lists, and tuples'))
         requestsdict[command] = packageNames
 
     make_v0(appsWithPackages, apks, repodir, repodict, requestsdict)
@@ -199,7 +203,7 @@ def make_v1(apps, packages, repodir, repodict, requestsdict):
     for package in packages:
         packageName = package['packageName']
         if packageName not in apps:
-            logging.info('Ignoring package without metadata: ' + package['apkName'])
+            logging.info(_('Ignoring package without metadata: ') + package['apkName'])
             continue
         if packageName in output_packages:
             packagelist = output_packages[packageName]
@@ -224,7 +228,7 @@ def make_v1(apps, packages, repodir, repodict, requestsdict):
             json.dump(output, fp, default=_index_encoder_default)
 
     if common.options.nosign:
-        logging.debug('index-v1 must have a signature, use `fdroid signindex` to create it!')
+        logging.debug(_('index-v1 must have a signature, use `fdroid signindex` to create it!'))
     else:
         signindex.config = common.config
         signindex.sign_index_v1(repodir, json_name)
@@ -501,9 +505,9 @@ def make_v0(apps, apks, repodir, repodict, requestsdict):
     if 'repo_keyalias' in common.config:
 
         if common.options.nosign:
-            logging.info("Creating unsigned index in preparation for signing")
+            logging.info(_("Creating unsigned index in preparation for signing"))
         else:
-            logging.info("Creating signed index with this key (SHA256):")
+            logging.info(_("Creating signed index with this key (SHA256):"))
             logging.info("%s" % repo_pubkey_fingerprint)
 
         # Create a jar of the index...
@@ -613,7 +617,7 @@ def download_repo_index(url_str, etag=None, verify_fingerprint=True):
     if verify_fingerprint:
         query = urllib.parse.parse_qs(url.query)
         if 'fingerprint' not in query:
-            raise VerificationException("No fingerprint in URL.")
+            raise VerificationException(_("No fingerprint in URL."))
         fingerprint = query['fingerprint'][0]
 
     url = urllib.parse.SplitResult(url.scheme, url.netloc, url.path + '/index-v1.jar', '', '')
@@ -635,7 +639,7 @@ def download_repo_index(url_str, etag=None, verify_fingerprint=True):
 
         # compare the fingerprint if verify_fingerprint is True
         if verify_fingerprint and fingerprint.upper() != public_key_fingerprint:
-            raise VerificationException("The repository's fingerprint does not match.")
+            raise VerificationException(_("The repository's fingerprint does not match."))
 
         # load repository index from JSON
         index = json.loads(jar.read('index-v1.json').decode("utf-8"))
@@ -655,7 +659,7 @@ def verify_jar_signature(file):
     :raises: VerificationException() if the JAR's signature could not be verified
     """
     if not common.verify_apk_signature(file, jar=True):
-        raise VerificationException("The repository's index could not be verified.")
+        raise VerificationException(_("The repository's index could not be verified."))
 
 
 def get_public_key_from_jar(jar):
@@ -670,9 +674,9 @@ def get_public_key_from_jar(jar):
     # extract certificate from jar
     certs = [n for n in jar.namelist() if common.CERT_PATH_REGEX.match(n)]
     if len(certs) < 1:
-        raise VerificationException("Found no signing certificates for repository.")
+        raise VerificationException(_("Found no signing certificates for repository."))
     if len(certs) > 1:
-        raise VerificationException("Found multiple signing certificates for repository.")
+        raise VerificationException(_("Found multiple signing certificates for repository."))
 
     # extract public key from certificate
     public_key = common.get_certificate(jar.read(certs[0]))
index 5a89546437ea6dc4a4c5806b5e9c83201cdaae8e..a115f64c717cc4cb74d3b25306d3545be79dd884 100644 (file)
@@ -27,6 +27,7 @@ import sys
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from .exception import FDroidException
 
@@ -53,15 +54,15 @@ def main():
     parser = ArgumentParser()
     common.setup_global_opts(parser)
     parser.add_argument("-d", "--distinguished-name", default=None,
-                        help="X.509 'Distiguished Name' used when generating keys")
+                        help=_("X.509 'Distiguished Name' used when generating keys"))
     parser.add_argument("--keystore", default=None,
-                        help="Path to the keystore for the repo signing key")
+                        help=_("Path to the keystore for the repo signing key"))
     parser.add_argument("--repo-keyalias", default=None,
-                        help="Alias of the repo signing key in the keystore")
+                        help=_("Alias of the repo signing key in the keystore"))
     parser.add_argument("--android-home", default=None,
-                        help="Path to the Android SDK (sometimes set in ANDROID_HOME)")
+                        help=_("Path to the Android SDK (sometimes set in ANDROID_HOME)"))
     parser.add_argument("--no-prompt", action="store_true", default=False,
-                        help="Do not prompt for Android SDK path, just fail")
+                        help=_("Do not prompt for Android SDK path, just fail"))
     options = parser.parse_args()
 
     # find root install prefix
@@ -106,8 +107,7 @@ def main():
                                                 'AppData', 'Local', 'Android', 'android-sdk')
             while not options.no_prompt:
                 try:
-                    s = input('Enter the path to the Android SDK ('
-                              + default_sdk_path + ') here:\n> ')
+                    s = input(_('Enter the path to the Android SDK (%s) here:\n> ') % default_sdk_path)
                 except KeyboardInterrupt:
                     print('')
                     sys.exit(1)
@@ -231,21 +231,20 @@ def main():
         common.write_to_config(test_config, 'keydname', c['keydname'])
         common.genkeystore(c)
 
-    logging.info('Built repo based in "' + fdroiddir + '"')
-    logging.info('with this config:')
-    logging.info('  Android SDK:\t\t\t' + config['sdk_path'])
+    msg = '\n'
+    msg += _('Built repo based in "%s" with this config:') % fdroiddir
+    msg += '\n\n  Android SDK:\t\t\t' + config['sdk_path']
     if aapt:
-        logging.info('  Android SDK Build Tools:\t' + os.path.dirname(aapt))
-    logging.info('  Android NDK r12b (optional):\t$ANDROID_NDK')
-    logging.info('  Keystore for signing key:\t' + keystore)
+        msg += '\n  Android SDK Build Tools:\t' + os.path.dirname(aapt)
+    msg += '\n  Android NDK r12b (optional):\t$ANDROID_NDK'
+    msg += '\n  ' + _('Keystore for signing key:\t') + keystore
     if repo_keyalias is not None:
-        logging.info('  Alias for key in store:\t' + repo_keyalias)
-    logging.info('\nTo complete the setup, add your APKs to "' +
-                 os.path.join(fdroiddir, 'repo') + '"' + '''
+        msg += '\n  Alias for key in store:\t' + repo_keyalias
+    msg += '\n\n' + '''To complete the setup, add your APKs to "%s"
 then run "fdroid update -c; fdroid update".  You might also want to edit
 "config.py" to set the URL, repo name, and more.  You should also set up
 a signing key (a temporary one might have been automatically generated).
 
 For more info: https://f-droid.org/docs/Setup_an_F-Droid_App_Repo
-and https://f-droid.org/docs/Signing_Process
-''')
+and https://f-droid.org/docs/Signing_Process''' % os.path.join(fdroiddir, 'repo')
+    logging.info(msg)
index 9f1264284b6e85e2c853bef495bb175a5d51e239..e3e21ff5a4dfe5a0f5da01ce16d9c820aa70b058 100644 (file)
@@ -23,6 +23,7 @@ import glob
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from .common import SdkToolsPopen
 from .exception import FDroidException
@@ -49,19 +50,19 @@ def main():
     # Parse command line...
     parser = ArgumentParser(usage="%(prog)s [options] [APPID[:VERCODE] [APPID[:VERCODE] ...]]")
     common.setup_global_opts(parser)
-    parser.add_argument("appid", nargs='*', help="app-id with optional versionCode in the form APPID[:VERCODE]")
+    parser.add_argument("appid", nargs='*', help=_("applicationId with optional versionCode in the form APPID[:VERCODE]"))
     parser.add_argument("-a", "--all", action="store_true", default=False,
-                        help="Install all signed applications available")
+                        help=_("Install all signed applications available"))
     options = parser.parse_args()
 
     if not options.appid and not options.all:
-        parser.error("option %s: If you really want to install all the signed apps, use --all" % "all")
+        parser.error(_("option %s: If you really want to install all the signed apps, use --all") % "all")
 
     config = common.read_config(options)
 
     output_dir = 'repo'
     if not os.path.isdir(output_dir):
-        logging.info("No signed output directory - nothing to do")
+        logging.info(_("No signed output directory - nothing to do"))
         sys.exit(0)
 
     if options.appid:
@@ -84,7 +85,7 @@ def main():
 
         for appid, apk in apks.items():
             if not apk:
-                raise FDroidException("No signed apk available for %s" % appid)
+                raise FDroidException(_("No signed apk available for %s") % appid)
 
     else:
 
@@ -95,10 +96,10 @@ def main():
         # Get device list each time to avoid device not found errors
         devs = devices()
         if not devs:
-            raise FDroidException("No attached devices found")
-        logging.info("Installing %s..." % apk)
+            raise FDroidException(_("No attached devices found"))
+        logging.info(_("Installing %s...") % apk)
         for dev in devs:
-            logging.info("Installing %s on %s..." % (apk, dev))
+            logging.info(_("Installing %s on %s...") % (apk, dev))
             p = SdkToolsPopen(['adb', "-s", dev, "install", apk])
             fail = ""
             for line in p.output.splitlines():
@@ -108,12 +109,12 @@ def main():
                 continue
 
             if fail == "INSTALL_FAILED_ALREADY_EXISTS":
-                logging.warn("%s is already installed on %s." % (apk, dev))
+                logging.warn(_("%s is already installed on %s.") % (apk, dev))
             else:
-                raise FDroidException("Failed to install %s on %s: %s" % (
+                raise FDroidException(_("Failed to install %s on %s: %s") % (
                     apk, dev, fail))
 
-    logging.info("\nFinished")
+    logging.info('\n' + _('Finished'))
 
 
 if __name__ == "__main__":
index 04ef0f9cbbdb202cddfe2bcfaff1472b48256e75..492a09fc05a87b607077a10f1e063bc39516f438 100644 (file)
@@ -22,6 +22,7 @@ import os
 import re
 import sys
 
+from . import _
 from . import common
 from . import metadata
 from . import rewritemeta
@@ -47,7 +48,7 @@ https_enforcings = [
 
 def forbid_shortener(domain):
     return (re.compile(r'https?://[^/]*' + re.escape(domain) + r'/.*'),
-            "URL shorteners should not be used")
+            _("URL shorteners should not be used"))
 
 
 http_url_shorteners = [
@@ -62,9 +63,9 @@ http_url_shorteners = [
 
 http_checks = https_enforcings + http_url_shorteners + [
     (re.compile(r'.*github\.com/[^/]+/[^/]+\.git'),
-     "Appending .git is not necessary"),
+     _("Appending .git is not necessary")),
     (re.compile(r'.*://[^/]*(github|gitlab|bitbucket|rawgit)[^/]*/([^/]+/){1,3}master'),
-     "Use /HEAD instead of /master to point at a file in the default branch"),
+     _("Use /HEAD instead of /master to point at a file in the default branch")),
 ]
 
 regex_checks = {
@@ -73,44 +74,44 @@ regex_checks = {
     'Repo': https_enforcings,
     'IssueTracker': http_checks + [
         (re.compile(r'.*github\.com/[^/]+/[^/]+/*$'),
-         "/issues is missing"),
+         _("/issues is missing")),
         (re.compile(r'.*gitlab\.com/[^/]+/[^/]+/*$'),
-         "/issues is missing"),
+         _("/issues is missing")),
     ],
     'Donate': http_checks + [
         (re.compile(r'.*flattr\.com'),
-         "Flattr donation methods belong in the FlattrID flag"),
+         _("Flattr donation methods belong in the FlattrID flag")),
     ],
     'Changelog': http_checks,
     'Author Name': [
         (re.compile(r'^\s'),
-         "Unnecessary leading space"),
+         _("Unnecessary leading space")),
         (re.compile(r'.*\s$'),
-         "Unnecessary trailing space"),
+         _("Unnecessary trailing space")),
     ],
     'Summary': [
         (re.compile(r'.*\b(free software|open source)\b.*', re.IGNORECASE),
-         "No need to specify that the app is Free Software"),
+         _("No need to specify that the app is Free Software")),
         (re.compile(r'.*((your|for).*android|android.*(app|device|client|port|version))', re.IGNORECASE),
-         "No need to specify that the app is for Android"),
+         _("No need to specify that the app is for Android")),
         (re.compile(r'.*[a-z0-9][.!?]( |$)'),
-         "Punctuation should be avoided"),
+         _("Punctuation should be avoided")),
         (re.compile(r'^\s'),
-         "Unnecessary leading space"),
+         _("Unnecessary leading space")),
         (re.compile(r'.*\s$'),
-         "Unnecessary trailing space"),
+         _("Unnecessary trailing space")),
     ],
     'Description': [
         (re.compile(r'\s*[*#][^ .]'),
-         "Invalid bulleted list"),
+         _("Invalid bulleted list")),
         (re.compile(r'^\s'),
-         "Unnecessary leading space"),
+         _("Unnecessary leading space")),
         (re.compile(r'.*\s$'),
-         "Unnecessary trailing space"),
+         _("Unnecessary trailing space")),
         (re.compile(r'.*([^[]|^)\[[^:[\]]+( |\]|$)'),
-         "Invalid link - use [http://foo.bar Link title] or [http://foo.bar]"),
+         _("Invalid link - use [http://foo.bar Link title] or [http://foo.bar]")),
         (re.compile(r'(^|.* )https?://[^ ]+'),
-         "Unlinkified link - use [http://foo.bar Link title] or [http://foo.bar]"),
+         _("Unlinkified link - use [http://foo.bar Link title] or [http://foo.bar]")),
     ],
 }
 
@@ -155,7 +156,7 @@ def check_ucm_tags(app):
             and lastbuild.versionCode == app.CurrentVersionCode
             and not lastbuild.forcevercode
             and any(s in lastbuild.commit for s in '.,_-/')):
-        yield "Last used commit '%s' looks like a tag, but Update Check Mode is '%s'" % (
+        yield _("Last used commit '%s' looks like a tag, but Update Check Mode is '%s'") % (
             lastbuild.commit, app.UpdateCheckMode)
 
 
@@ -163,11 +164,11 @@ def check_char_limits(app):
     limits = config['char_limits']
 
     if len(app.Summary) > limits['summary']:
-        yield "Summary of length %s is over the %i char limit" % (
+        yield _("Summary of length %s is over the %i char limit") % (
             len(app.Summary), limits['summary'])
 
     if len(app.Description) > limits['description']:
-        yield "Description of length %s is over the %i char limit" % (
+        yield _("Description of length %s is over the %i char limit") % (
             len(app.Description), limits['description'])
 
 
@@ -185,12 +186,12 @@ def check_old_links(app):
         for f in ['WebSite', 'SourceCode', 'IssueTracker', 'Changelog']:
             v = app.get(f)
             if any(s in v for s in old_sites):
-                yield "App is in '%s' but has a link to '%s'" % (app.Repo, v)
+                yield _("App is in '%s' but has a link to '%s'") % (app.Repo, v)
 
 
 def check_useless_fields(app):
     if app.UpdateCheckName == app.id:
-        yield "Update Check Name is set to the known app id - it can be removed"
+        yield _("Update Check Name is set to the known app id - it can be removed")
 
 
 filling_ucms = re.compile(r'^(Tags.*|RepoManifest.*)')
@@ -199,12 +200,12 @@ filling_ucms = re.compile(r'^(Tags.*|RepoManifest.*)')
 def check_checkupdates_ran(app):
     if filling_ucms.match(app.UpdateCheckMode):
         if not app.AutoName and not app.CurrentVersion and app.CurrentVersionCode == '0':
-            yield "UCM is set but it looks like checkupdates hasn't been run yet"
+            yield _("UCM is set but it looks like checkupdates hasn't been run yet")
 
 
 def check_empty_fields(app):
     if not app.Categories:
-        yield "Categories are not set"
+        yield _("Categories are not set")
 
 
 all_categories = set([
@@ -231,12 +232,12 @@ all_categories = set([
 def check_categories(app):
     for categ in app.Categories:
         if categ not in all_categories:
-            yield "Category '%s' is not valid" % categ
+            yield _("Category '%s' is not valid" % categ)
 
 
 def check_duplicates(app):
     if app.Name and app.Name == app.AutoName:
-        yield "Name '%s' is just the auto name - remove it" % app.Name
+        yield _("Name '%s' is just the auto name - remove it") % app.Name
 
     links_seen = set()
     for f in ['Source Code', 'Web Site', 'Issue Tracker', 'Changelog']:
@@ -245,25 +246,25 @@ def check_duplicates(app):
             continue
         v = v.lower()
         if v in links_seen:
-            yield "Duplicate link in '%s': %s" % (f, v)
+            yield _("Duplicate link in '%s': %s") % (f, v)
         else:
             links_seen.add(v)
 
     name = app.Name or app.AutoName
     if app.Summary and name:
         if app.Summary.lower() == name.lower():
-            yield "Summary '%s' is just the app's name" % app.Summary
+            yield _("Summary '%s' is just the app's name") % app.Summary
 
     if app.Summary and app.Description and len(app.Description) == 1:
         if app.Summary.lower() == app.Description[0].lower():
-            yield "Description '%s' is just the app's summary" % app.Summary
+            yield _("Description '%s' is just the app's summary") % app.Summary
 
     seenlines = set()
     for l in app.Description.splitlines():
         if len(l) < 1:
             continue
         if l in seenlines:
-            yield "Description has a duplicate line"
+            yield _("Description has a duplicate line")
         seenlines.add(l)
 
 
@@ -276,7 +277,7 @@ def check_mediawiki_links(app):
         url = um.group(1)
         for m, r in http_checks:
             if m.match(url):
-                yield "URL '%s' in Description: %s" % (url, r)
+                yield _("URL '%s' in Description: %s") % (url, r)
 
 
 def check_bulleted_lists(app):
@@ -291,7 +292,7 @@ def check_bulleted_lists(app):
         if l[0] == lchar and l[1] == ' ':
             lcount += 1
             if lcount > 2 and lchar not in validchars:
-                yield "Description has a list (%s) but it isn't bulleted (*) nor numbered (#)" % lchar
+                yield _("Description has a list (%s) but it isn't bulleted (*) nor numbered (#)") % lchar
                 break
         else:
             lchar = l[0]
@@ -304,18 +305,18 @@ def check_builds(app):
     for build in app.builds:
         if build.disable:
             if build.disable.startswith('Generated by import.py'):
-                yield "Build generated by `fdroid import` - remove disable line once ready"
+                yield _("Build generated by `fdroid import` - remove disable line once ready")
             continue
         for s in ['master', 'origin', 'HEAD', 'default', 'trunk']:
             if build.commit and build.commit.startswith(s):
-                yield "Branch '%s' used as commit in build '%s'" % (s, build.versionName)
+                yield _("Branch '%s' used as commit in build '%s'") % (s, build.versionName)
             for srclib in build.srclibs:
                 ref = srclib.split('@')[1].split('/')[0]
                 if ref.startswith(s):
-                    yield "Branch '%s' used as commit in srclib '%s'" % (s, srclib)
+                    yield _("Branch '%s' used as commit in srclib '%s'") % (s, srclib)
         for key in build.keys():
             if key not in supported_flags:
-                yield key + ' is not an accepted build field'
+                yield _('%s is not an accepted build field') % key
 
 
 def check_files_dir(app):
@@ -326,7 +327,7 @@ def check_files_dir(app):
     for name in os.listdir(dir_path):
         path = os.path.join(dir_path, name)
         if not (os.path.isfile(path) or name == 'signatures' or locale_pattern.match(name)):
-            yield "Found non-file at %s" % path
+            yield _("Found non-file at %s") % path
             continue
         files.add(name)
 
@@ -334,45 +335,45 @@ def check_files_dir(app):
     for build in app.builds:
         for fname in build.patch:
             if fname not in files:
-                yield "Unknown file %s in build '%s'" % (fname, build.versionName)
+                yield _("Unknown file %s in build '%s'") % (fname, build.versionName)
             else:
                 used.add(fname)
 
     for name in files.difference(used):
         if locale_pattern.match(name):
             continue
-        yield "Unused file at %s" % os.path.join(dir_path, name)
+        yield _("Unused file at %s") % os.path.join(dir_path, name)
 
 
 def check_format(app):
     if options.format and not rewritemeta.proper_format(app):
-        yield "Run rewritemeta to fix formatting"
+        yield _("Run rewritemeta to fix formatting")
 
 
 def check_license_tag(app):
     '''Ensure all license tags are in https://spdx.org/license-list'''
     if app.License.rstrip('+') not in SPDX:
-        yield 'Invalid license tag "%s"! Use only tags from https://spdx.org/license-list' \
+        yield _('Invalid license tag "%s"! Use only tags from https://spdx.org/license-list') \
             % (app.License)
 
 
 def check_extlib_dir(apps):
     dir_path = os.path.join('build', 'extlib')
-    files = set()
-    for root, dirs, names in os.walk(dir_path):
-        for name in names:
-            files.add(os.path.join(root, name)[len(dir_path) + 1:])
+    unused_extlib_files = set()
+    for root, dirs, files in os.walk(dir_path):
+        for name in files:
+            unused_extlib_files.add(os.path.join(root, name)[len(dir_path) + 1:])
 
     used = set()
     for app in apps:
         for build in app.builds:
             for path in build.extlibs:
-                if path not in files:
-                    yield "%s: Unknown extlib %s in build '%s'" % (app.id, path, build.versionName)
+                if path not in unused_extlib_files:
+                    yield _("%s: Unknown extlib %s in build '%s'") % (app.id, path, build.versionName)
                 else:
                     used.add(path)
 
-    for path in files.difference(used):
+    for path in unused_extlib_files.difference(used):
         if any(path.endswith(s) for s in [
                 '.gitignore',
                 'source.txt', 'origin.txt', 'md5.txt',
@@ -381,7 +382,7 @@ def check_extlib_dir(apps):
                 'NOTICE', 'NOTICE.txt',
                 ]):
             continue
-        yield "Unused extlib at %s" % os.path.join(dir_path, path)
+        yield _("Unused extlib at %s") % os.path.join(dir_path, path)
 
 
 def check_for_unsupported_metadata_files(basedir=""):
@@ -397,7 +398,7 @@ def check_for_unsupported_metadata_files(basedir=""):
             for t in formats:
                 exists = exists or os.path.exists(f + '.' + t)
             if not exists:
-                print('"' + f + '/" has no matching metadata file!')
+                print(_('"%s/" has no matching metadata file!') % f)
                 return_value = True
         elif not os.path.splitext(f)[1][1:] in formats:
             print('"' + f.replace(basedir, '')
@@ -415,8 +416,8 @@ def main():
     parser = ArgumentParser(usage="%(prog)s [options] [APPID [APPID ...]]")
     common.setup_global_opts(parser)
     parser.add_argument("-f", "--format", action="store_true", default=False,
-                        help="Also warn about formatting issues, like rewritemeta -l")
-    parser.add_argument("appid", nargs='*', help="app-id in the form APPID")
+                        help=_("Also warn about formatting issues, like rewritemeta -l"))
+    parser.add_argument("appid", nargs='*', help=_("applicationId in the form APPID"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
index 25e0537705411cd6c7fdb18f52e19344d0e46db3..d1602d9ebeea18d2341e8aa294d17d624fd247cb 100644 (file)
@@ -35,6 +35,7 @@ except ImportError:
     YamlLoader = Loader
 
 import fdroidserver.common
+from fdroidserver import _
 from fdroidserver.exception import MetaDataException, FDroidException
 
 srclibs = None
@@ -1519,4 +1520,4 @@ def write_metadata(metadatapath, app):
 def add_metadata_arguments(parser):
     '''add common command line flags related to metadata processing'''
     parser.add_argument("-W", default='error',
-                        help="force errors to be warnings, or ignore")
+                        help=_("force errors to be warnings, or ignore"))
index ad2009067041a1616aaadf3d12143af2bfff38aa..94cf166b45b78d15b0cfc60c6bede6061dc590a5 100644 (file)
@@ -26,6 +26,7 @@ import hashlib
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from . import metadata
 from .common import FDroidPopen, SdkToolsPopen
@@ -43,7 +44,7 @@ def main():
     parser = ArgumentParser(usage="%(prog)s [options] "
                             "[APPID[:VERCODE] [APPID[:VERCODE] ...]]")
     common.setup_global_opts(parser)
-    parser.add_argument("appid", nargs='*', help="app-id with optional versionCode in the form APPID[:VERCODE]")
+    parser.add_argument("appid", nargs='*', help=_("applicationId with optional versionCode in the form APPID[:VERCODE]"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -51,27 +52,27 @@ def main():
     config = common.read_config(options)
 
     if not ('jarsigner' in config and 'keytool' in config):
-        logging.critical('Java JDK not found! Install in standard location or set java_paths!')
+        logging.critical(_('Java JDK not found! Install in standard location or set java_paths!'))
         sys.exit(1)
 
     log_dir = 'logs'
     if not os.path.isdir(log_dir):
-        logging.info("Creating log directory")
+        logging.info(_("Creating log directory"))
         os.makedirs(log_dir)
 
     tmp_dir = 'tmp'
     if not os.path.isdir(tmp_dir):
-        logging.info("Creating temporary directory")
+        logging.info(_("Creating temporary directory"))
         os.makedirs(tmp_dir)
 
     output_dir = 'repo'
     if not os.path.isdir(output_dir):
-        logging.info("Creating output directory")
+        logging.info(_("Creating output directory"))
         os.makedirs(output_dir)
 
     unsigned_dir = 'unsigned'
     if not os.path.isdir(unsigned_dir):
-        logging.warning("No unsigned directory - nothing to do")
+        logging.warning(_("No unsigned directory - nothing to do"))
         sys.exit(1)
 
     if not os.path.exists(config['keystore']):
@@ -95,7 +96,7 @@ def main():
         m.update(appid.encode('utf-8'))
         keyalias = m.hexdigest()[:8]
         if keyalias in allaliases:
-            logging.error("There is a keyalias collision - publishing halted")
+            logging.error(_("There is a keyalias collision - publishing halted"))
             sys.exit(1)
         allaliases.append(keyalias)
     logging.info("{0} apps, {0} key aliases".format(len(allapps),
@@ -208,13 +209,13 @@ def main():
                              'SHA1withRSA', '-digestalg', 'SHA1',
                              apkfile, keyalias], envs=env_vars)
             if p.returncode != 0:
-                raise BuildException("Failed to sign application")
+                raise BuildException(_("Failed to sign application"))
 
             # Zipalign it...
             p = SdkToolsPopen(['zipalign', '-v', '4', apkfile,
                                os.path.join(output_dir, apkfilename)])
             if p.returncode != 0:
-                raise BuildException("Failed to align application")
+                raise BuildException(_("Failed to align application"))
             os.remove(apkfile)
 
         # Move the source tarball into the output directory...
index ae4627ebdd8936a21174b34c6a950629797bccc7..776e1f14c41e034b1db3f6e80b908d63d86668c5 100644 (file)
@@ -22,6 +22,7 @@ import os
 import logging
 import io
 
+from . import _
 from . import common
 from . import metadata
 
@@ -51,10 +52,10 @@ def main():
     parser = ArgumentParser(usage="%(prog)s [options] [APPID [APPID ...]]")
     common.setup_global_opts(parser)
     parser.add_argument("-l", "--list", action="store_true", default=False,
-                        help="List files that would be reformatted")
+                        help=_("List files that would be reformatted"))
     parser.add_argument("-t", "--to", default=None,
-                        help="Rewrite to a specific format: " + ', '.join(supported))
-    parser.add_argument("appid", nargs='*', help="app-id in the form APPID")
+                        help=_("Rewrite to a specific format: ") + ', '.join(supported))
+    parser.add_argument("appid", nargs='*', help=_("applicationId in the form APPID"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -66,21 +67,21 @@ def main():
     apps = common.read_app_args(options.appid, allapps, False)
 
     if options.list and options.to is not None:
-        parser.error("Cannot use --list and --to at the same time")
+        parser.error(_("Cannot use --list and --to at the same time"))
 
     if options.to is not None and options.to not in supported:
-        parser.error("Unsupported metadata format, use: --to [" + ' '.join(supported) + "]")
+        parser.error(_("Unsupported metadata format, use: --to [%s]") % ' '.join(supported))
 
     for appid, app in apps.items():
         path = app.metadatapath
         base, ext = common.get_extension(path)
         if not options.to and ext not in supported:
-            logging.info("Ignoring %s file at '%s'" % (ext, path))
+            logging.info(_("Ignoring %s file at '%s'") % (ext, path))
             continue
         elif options.to is not None:
-            logging.info("rewriting '%s' to %s" % (appid, options.to))
+            logging.info(_("rewriting '%s' to %s") % (appid, options.to))
         else:
-            logging.info("rewriting '%s'" % (appid))
+            logging.info(_("rewriting '%s'") % (appid))
 
         to_ext = ext
         if options.to is not None:
@@ -107,7 +108,7 @@ def main():
         if ext != to_ext:
             os.remove(path)
 
-    logging.debug("Finished.")
+    logging.debug(_("Finished"))
 
 
 if __name__ == "__main__":
index f768e6788a98d6f9a1cdc5d89f512df88d3a3ef4..874f0b0081d72e489981ea74cc73a89f02a2c7fb 100644 (file)
@@ -22,6 +22,7 @@ import traceback
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from . import metadata
 from .exception import BuildException, VCSException
@@ -165,20 +166,20 @@ def scan_source(build_dir, build):
         return any(command.match(line) for command in gradle_compile_commands)
 
     # Iterate through all files in the source code
-    for dirpath, dirnames, filenames in os.walk(build_dir, topdown=True):
+    for root, dirs, files in os.walk(build_dir, topdown=True):
 
         # It's topdown, so checking the basename is enough
         for ignoredir in ('.hg', '.git', '.svn', '.bzr'):
-            if ignoredir in dirnames:
-                dirnames.remove(ignoredir)
+            if ignoredir in dirs:
+                dirs.remove(ignoredir)
 
-        for curfile in filenames:
+        for curfile in files:
 
             if curfile in ['.DS_Store']:
                 continue
 
             # Path (relative) to the file
-            filepath = os.path.join(dirpath, curfile)
+            filepath = os.path.join(root, curfile)
 
             if os.path.islink(filepath):
                 continue
@@ -256,7 +257,7 @@ def main():
     # Parse command line...
     parser = ArgumentParser(usage="%(prog)s [options] [APPID[:VERCODE] [APPID[:VERCODE] ...]]")
     common.setup_global_opts(parser)
-    parser.add_argument("appid", nargs='*', help="app-id with optional versionCode in the form APPID[:VERCODE]")
+    parser.add_argument("appid", nargs='*', help=_("applicationId with optional versionCode in the form APPID[:VERCODE]"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -329,8 +330,8 @@ def main():
                 appid, traceback.format_exc()))
             probcount += 1
 
-    logging.info("Finished:")
-    print("%d problems found" % probcount)
+    logging.info(_("Finished"))
+    print(_("%d problems found") % probcount)
 
 
 if __name__ == "__main__":
index 3b36eceeac96a8fc110bfa3f99ec932b9ebc1c0e..aa15284f6e28f5161ba4270b6803571c09e119a9 100644 (file)
@@ -29,6 +29,7 @@ from argparse import ArgumentParser
 import logging
 import shutil
 
+from . import _
 from . import common
 from .exception import FDroidException
 
@@ -154,7 +155,7 @@ def update_awsbucket_libcloud(repo_section):
         if obj.name.startswith(upload_dir + '/'):
             objs[obj.name] = obj
 
-    for root, _, files in os.walk(os.path.join(os.getcwd(), repo_section)):
+    for root, dirs, files in os.walk(os.path.join(os.getcwd(), repo_section)):
         for name in files:
             upload = False
             file_to_upload = os.path.join(root, name)
@@ -307,9 +308,9 @@ def update_localcopy(repo_section, local_copy_dir):
 def _get_size(start_path='.'):
     '''get size of all files in a dir https://stackoverflow.com/a/1392549'''
     total_size = 0
-    for dirpath, dirnames, filenames in os.walk(start_path):
-        for f in filenames:
-            fp = os.path.join(dirpath, f)
+    for root, dirs, files in os.walk(start_path):
+        for f in files:
+            fp = os.path.join(root, f)
             total_size += os.path.getsize(fp)
     return total_size
 
@@ -577,19 +578,19 @@ def main():
     # Parse command line...
     parser = ArgumentParser()
     common.setup_global_opts(parser)
-    parser.add_argument("command", help="command to execute, either 'init' or 'update'")
+    parser.add_argument("command", help=_("command to execute, either 'init' or 'update'"))
     parser.add_argument("-i", "--identity-file", default=None,
-                        help="Specify an identity file to provide to SSH for rsyncing")
+                        help=_("Specify an identity file to provide to SSH for rsyncing"))
     parser.add_argument("--local-copy-dir", default=None,
-                        help="Specify a local folder to sync the repo to")
+                        help=_("Specify a local folder to sync the repo to"))
     parser.add_argument("--no-checksum", action="store_true", default=False,
-                        help="Don't use rsync checksums")
+                        help=_("Don't use rsync checksums"))
     options = parser.parse_args()
 
     config = common.read_config(options)
 
     if options.command != 'init' and options.command != 'update':
-        logging.critical("The only commands currently supported are 'init' and 'update'")
+        logging.critical(_("The only commands currently supported are 'init' and 'update'"))
         sys.exit(1)
 
     if config.get('nonstandardwebroot') is True:
@@ -605,7 +606,7 @@ def main():
         elif len(s) == 2:
             host, fdroiddir = s
         else:
-            logging.error('Malformed serverwebroot line: ' + serverwebroot)
+            logging.error(_('Malformed serverwebroot line:') + ' ' + serverwebroot)
             sys.exit(1)
         repobase = os.path.basename(fdroiddir)
         if standardwebroot and repobase != 'fdroid':
@@ -624,7 +625,7 @@ def main():
     if local_copy_dir is not None:
         fdroiddir = local_copy_dir.rstrip('/')
         if os.path.exists(fdroiddir) and not os.path.isdir(fdroiddir):
-            logging.error('local_copy_dir must be directory, not a file!')
+            logging.error(_('local_copy_dir must be directory, not a file!'))
             sys.exit(1)
         if not os.path.exists(os.path.dirname(fdroiddir)):
             logging.error('The root dir for local_copy_dir "'
@@ -632,7 +633,7 @@ def main():
                           + '" does not exist!')
             sys.exit(1)
         if not os.path.isabs(fdroiddir):
-            logging.error('local_copy_dir must be an absolute path!')
+            logging.error(_('local_copy_dir must be an absolute path!'))
             sys.exit(1)
         repobase = os.path.basename(fdroiddir)
         if standardwebroot and repobase != 'fdroid':
@@ -652,8 +653,8 @@ def main():
             and not config.get('binary_transparency_remote') \
             and not config.get('virustotal_apikey') \
             and local_copy_dir is None:
-        logging.warn('No option set! Edit your config.py to set at least one among:\n'
-                     + 'serverwebroot, servergitmirrors, local_copy_dir, awsbucket, virustotal_apikey, androidobservatory, or binary_transparency_remote')
+        logging.warn(_('No option set! Edit your config.py to set at least one of these:')
+                     + '\nserverwebroot, servergitmirrors, local_copy_dir, awsbucket, virustotal_apikey, androidobservatory, or binary_transparency_remote')
         sys.exit(1)
 
     repo_sections = ['repo']
index 298711ae66dc749cb09c0dbfb45763ebacaf8e71..fb6c53a9adcc0547ab7495b2d522182b147fb16d 100644 (file)
@@ -22,6 +22,7 @@ import os
 import sys
 import logging
 
+from . import _
 from . import common
 from . import net
 from .exception import FDroidException
@@ -52,7 +53,7 @@ def extract(config, options):
         os.mkdir(tmp_dir)
 
     if not options.APK or len(options.APK) <= 0:
-        logging.critical('no APK supplied')
+        logging.critical(_('no APK supplied'))
         sys.exit(1)
 
     # iterate over supplied APKs downlaod and extract them...
@@ -61,21 +62,21 @@ def extract(config, options):
         try:
             if os.path.isfile(apk):
                 sigdir = extract_signature(apk)
-                logging.info('fetched singatures for %s -> %s', apk, sigdir)
+                logging.info(_('fetched signatures for %s -> %s'), apk, sigdir)
             elif httpre.match(apk):
                 if apk.startswith('https') or options.no_check_https:
                     try:
                         tmp_apk = os.path.join(tmp_dir, 'signed.apk')
                         net.download_file(apk, tmp_apk)
                         sigdir = extract_signature(tmp_apk)
-                        logging.info('fetched singatures for %s -> %s', apk, sigdir)
+                        logging.info(_('fetched signatures for %s -> %s'), apk, sigdir)
                     finally:
                         if tmp_apk and os.path.exists(tmp_apk):
                             os.remove(tmp_apk)
                 else:
-                    logging.warn('refuse downloading via insecure http connection (use https or specify --no-https-check): %s', apk)
+                    logging.warn(_('refuse downloading via insecure http connection (use https or specify --no-https-check): %s'), apk)
         except FDroidException as e:
-            logging.warning("failed fetching signatures for '%s': %s", apk, e)
+            logging.warning(_("failed fetching signatures for '%s': %s"), apk, e)
             if e.detail:
                 logging.debug(e.detail)
 
@@ -88,7 +89,7 @@ def main():
     parser = ArgumentParser(usage="%(prog)s [options] APK [APK...]")
     common.setup_global_opts(parser)
     parser.add_argument("APK", nargs='*',
-                        help="signed APK, either a file-path or Https-URL are fine here.")
+                        help=_("signed APK, either a file-path or HTTPS URL."))
     parser.add_argument("--no-check-https", action="store_true", default=False)
     options = parser.parse_args()
 
index 6b5dd98312815d43f366184b8770ec7714d58eb6..cbd552393539e26a83b6472f6dadee46a6662455 100644 (file)
@@ -21,6 +21,7 @@ import zipfile
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from .exception import FDroidException
 
@@ -87,7 +88,7 @@ def main():
 
     if 'jarsigner' not in config:
         raise FDroidException(
-            'Java jarsigner not found! Install in standard location or set java_paths!')
+            _('Java jarsigner not found! Install in standard location or set java_paths!'))
 
     repodirs = ['repo']
     if config['archive_older'] != 0:
@@ -114,7 +115,7 @@ def main():
             signed += 1
 
     if signed == 0:
-        logging.info("Nothing to do")
+        logging.info(_("Nothing to do"))
 
 
 if __name__ == "__main__":
index 8a1aec2a4322de6e6a8a6b1e8c6ee5bb73d3d35d..e87db35facdd54a7d995a4a0a6b52d3246ffcc2d 100644 (file)
@@ -30,6 +30,7 @@ import logging
 import subprocess
 from collections import Counter
 
+from . import _
 from . import common
 from . import metadata
 
@@ -61,12 +62,12 @@ def main():
     parser = ArgumentParser()
     common.setup_global_opts(parser)
     parser.add_argument("-d", "--download", action="store_true", default=False,
-                        help="Download logs we don't have")
+                        help=_("Download logs we don't have"))
     parser.add_argument("--recalc", action="store_true", default=False,
-                        help="Recalculate aggregate stats - use when changes "
-                        "have been made that would invalidate old cached data.")
+                        help=_("Recalculate aggregate stats - use when changes "
+                               "have been made that would invalidate old cached data."))
     parser.add_argument("--nologs", action="store_true", default=False,
-                        help="Don't do anything logs-related")
+                        help=_("Don't do anything logs-related"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -171,10 +172,10 @@ def main():
                     uri = match.group('uri')
                     if not uri.endswith('.apk'):
                         continue
-                    _, apkname = os.path.split(uri)
+                    _ignored, apkname = os.path.split(uri)
                     app = knownapks.getapp(apkname)
                     if app:
-                        appid, _ = app
+                        appid, _ignored = app
                         today['apps'][appid] += 1
                         # Strip the '.apk' from apkname
                         appver = apkname[:-4]
@@ -298,7 +299,7 @@ def main():
         for apk in unknownapks:
             logging.info(apk)
 
-    logging.info("Finished.")
+    logging.info(_("Finished"))
 
 
 if __name__ == "__main__":
index 3d1635a21e51dbb78fab85c04dfcde4e1c41909b..2c46b7f29b99bfb81296da178d04901f112d272f 100644 (file)
@@ -37,6 +37,7 @@ from binascii import hexlify
 from PIL import Image
 import logging
 
+from . import _
 from . import common
 from . import index
 from . import metadata
@@ -1697,34 +1698,34 @@ def main():
     parser = ArgumentParser()
     common.setup_global_opts(parser)
     parser.add_argument("--create-key", action="store_true", default=False,
-                        help="Create a repo signing key in a keystore")
+                        help=_("Create a repo signing key in a keystore"))
     parser.add_argument("-c", "--create-metadata", action="store_true", default=False,
-                        help="Create skeleton metadata files that are missing")
+                        help=_("Create skeleton metadata files that are missing"))
     parser.add_argument("--delete-unknown", action="store_true", default=False,
-                        help="Delete APKs and/or OBBs without metadata from the repo")
+                        help=_("Delete APKs and/or OBBs without metadata from the repo"))
     parser.add_argument("-b", "--buildreport", action="store_true", default=False,
-                        help="Report on build data status")
+                        help=_("Report on build data status"))
     parser.add_argument("-i", "--interactive", default=False, action="store_true",
-                        help="Interactively ask about things that need updating.")
+                        help=_("Interactively ask about things that need updating."))
     parser.add_argument("-I", "--icons", action="store_true", default=False,
-                        help="Resize all the icons exceeding the max pixel size and exit")
+                        help=_("Resize all the icons exceeding the max pixel size and exit"))
     parser.add_argument("-e", "--editor", default="/etc/alternatives/editor",
-                        help="Specify editor to use in interactive mode. Default " +
+                        help=_("Specify editor to use in interactive mode. Default ") +
                         "is /etc/alternatives/editor")
     parser.add_argument("-w", "--wiki", default=False, action="store_true",
-                        help="Update the wiki")
+                        help=_("Update the wiki"))
     parser.add_argument("--pretty", action="store_true", default=False,
-                        help="Produce human-readable index.xml")
+                        help=_("Produce human-readable index.xml"))
     parser.add_argument("--clean", action="store_true", default=False,
-                        help="Clean update - don't uses caches, reprocess all apks")
+                        help=_("Clean update - don't uses caches, reprocess all apks"))
     parser.add_argument("--nosign", action="store_true", default=False,
-                        help="When configured for signed indexes, create only unsigned indexes at this stage")
+                        help=_("When configured for signed indexes, create only unsigned indexes at this stage"))
     parser.add_argument("--use-date-from-apk", action="store_true", default=False,
-                        help="Use date from apk instead of current time for newly added apks")
+                        help=_("Use date from apk instead of current time for newly added apks"))
     parser.add_argument("--rename-apks", action="store_true", default=False,
-                        help="Rename APK files that do not match package.name_123.apk")
+                        help=_("Rename APK files that do not match package.name_123.apk"))
     parser.add_argument("--allow-disabled-algorithms", action="store_true", default=False,
-                        help="Include APKs that are signed with disabled algorithms like MD5")
+                        help=_("Include APKs that are signed with disabled algorithms like MD5"))
     metadata.add_metadata_arguments(parser)
     options = parser.parse_args()
     metadata.warnings_action = options.W
@@ -1899,7 +1900,7 @@ def main():
     if options.wiki:
         update_wiki(apps, sortedids, apks + archapks)
 
-    logging.info("Finished.")
+    logging.info(_("Finished"))
 
 
 if __name__ == "__main__":
index bd629f97061f0b2c242f4295255291e05cbfd0bf..9c4015583532b9aedb09854b91115d3175010331 100644 (file)
@@ -23,6 +23,7 @@ import requests
 from argparse import ArgumentParser
 import logging
 
+from . import _
 from . import common
 from . import net
 from .exception import FDroidException
@@ -38,19 +39,19 @@ def main():
     # Parse command line...
     parser = ArgumentParser(usage="%(prog)s [options] [APPID[:VERCODE] [APPID[:VERCODE] ...]]")
     common.setup_global_opts(parser)
-    parser.add_argument("appid", nargs='*', help="app-id with optional versionCode in the form APPID[:VERCODE]")
+    parser.add_argument("appid", nargs='*', help=_("applicationId with optional versionCode in the form APPID[:VERCODE]"))
     options = parser.parse_args()
 
     config = common.read_config(options)
 
     tmp_dir = 'tmp'
     if not os.path.isdir(tmp_dir):
-        logging.info("Creating temporary directory")
+        logging.info(_("Creating temporary directory"))
         os.makedirs(tmp_dir)
 
     unsigned_dir = 'unsigned'
     if not os.path.isdir(unsigned_dir):
-        logging.error("No unsigned directory - nothing to do")
+        logging.error(_("No unsigned directory - nothing to do"))
         sys.exit(0)
 
     verified = 0
@@ -83,7 +84,7 @@ def main():
                 try:
                     net.download_file(url.replace('/repo', '/archive'), dldir=tmp_dir)
                 except requests.exceptions.HTTPError as e:
-                    raise FDroidException('Downloading %s failed. %s', (url, e))
+                    raise FDroidException(_('Downloading %s failed. %s'), (url, e))
 
             compare_result = common.verify_apks(
                 remoteapk,
@@ -99,7 +100,7 @@ def main():
             logging.info("...NOT verified - {0}".format(e))
             notverified += 1
 
-    logging.info("Finished")
+    logging.info(_("Finished"))
     logging.info("{0} successfully verified".format(verified))
     logging.info("{0} NOT verified".format(notverified))
 
index 6ee8c3a8b60dbe19eaabd5424820e1032589f765..ef153039374dfb903ddc0eb3708e78ccbd4b8b39 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -17,6 +17,7 @@ setup(name='fdroidserver',
       author='The F-Droid Project',
       author_email='team@f-droid.org',
       url='https://f-droid.org',
+      license='AGPL-3.0',
       packages=['fdroidserver', 'fdroidserver.asynchronousfilereader'],
       scripts=['fdroid', 'fd-commit', 'makebuildserver'],
       data_files=[
@@ -29,6 +30,7 @@ setup(name='fdroidserver',
                'examples/public-read-only-s3-bucket-policy.json',
                'examples/template.yml']),
       ],
+      python_requires='>=3.4',
       install_requires=[
           'clint',
           'GitPython',
index 5d292f4b7e0dce25cf904193656af4282eb8c170..76a938027b6ab72c481c9629022ec14ace37fa0e 100755 (executable)
@@ -8,11 +8,13 @@ import logging
 import optparse
 import os
 import shutil
+import subprocess
 import sys
 import tempfile
 import unittest
 import yaml
 from binascii import unhexlify
+from distutils.version import LooseVersion
 
 localmodule = os.path.realpath(
     os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
@@ -397,8 +399,15 @@ class UpdateTest(unittest.TestCase):
             self.assertFalse(os.path.exists(os.path.join('archive', apkName)))
             self.assertTrue(os.path.exists(os.path.join('repo', apkName)))
 
+            javac = config['jarsigner'].replace('jarsigner', 'javac')
+            v = subprocess.check_output([javac, '-version'], stderr=subprocess.STDOUT)[6:-1].decode('utf-8')
+            if LooseVersion(v) < LooseVersion('1.8.0_132'):
+                print('SKIPPING: running tests with old Java (' + v + ')')
+                return
+
             # this test only works on systems with fully updated Java/jarsigner
             # that has MD5 listed in jdk.jar.disabledAlgorithms in java.security
+            # https://blogs.oracle.com/java-platform-group/oracle-jre-will-no-longer-trust-md5-signed-code-by-default
             skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
                                                                       knownapks,
                                                                       allow_disabled_algorithms=False,