mirror of
				https://github.com/f-droid/fdroidserver.git
				synced 2025-11-04 06:30:27 +03:00 
			
		
		
		
	Merge branch 'implement-gettext' into 'master'
first implementation of localization using gettext Closes #342 See merge request fdroid/fdroidserver!338
This commit is contained in:
		
						commit
						a2eaf37394
					
				
					 27 changed files with 335 additions and 272 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								fdroid
									
										
									
									
									
								
							
							
						
						
									
										50
									
								
								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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(r, root)
 | 
			
		||||
            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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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/'):]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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, '*.*'))):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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']
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								setup.py
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								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',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue