chiark / gitweb /
Merge branch 'doc-update-v1' into 'master'
authorCiaran Gultnieks <ciaran@ciarang.com>
Tue, 30 Dec 2014 22:48:58 +0000 (22:48 +0000)
committerCiaran Gultnieks <ciaran@ciarang.com>
Tue, 30 Dec 2014 22:48:58 +0000 (22:48 +0000)
Update documentation re: testing32 image

Use different subsections for the Debian base box setup (which can be bypassed using the prebuilt image) and the F-Droid / SDK setup (which cannot be bypassed).

Side note: would it be possible to offer a torrent for testing32.box?  The direct download is taking over 2 hours on a 50Mbps link.

See merge request !32

14 files changed:
buildserver/cookbooks/fdroidbuild-general/recipes/default.rb
examples/config.py
fdroidserver/build.py
fdroidserver/common.py
fdroidserver/init.py
fdroidserver/install.py
fdroidserver/lint.py
fdroidserver/publish.py
fdroidserver/update.py
tests/common.TestCase [new file with mode: 0755]
tests/install.TestCase [new file with mode: 0755]
tests/run-tests
tests/urzip-release-unsigned.apk [new file with mode: 0644]
tests/urzip-release.apk [new file with mode: 0644]

index 0693b1b38d0c80edeae5a6ad4e6018899f240bb5..7e6c94b347cee9ca50abbe872ad177edf6e59c4b 100644 (file)
@@ -5,7 +5,7 @@ execute "apt-get-update" do
   command "apt-get update"
 end
 
-%w{ant ant-contrib autoconf autopoint bison cmake expect libtool libsaxonb-java libssl1.0.0 libssl-dev maven openjdk-7-jdk javacc python python-magic git-core mercurial subversion bzr git-svn make perlmagick pkg-config zip yasm inkscape imagemagick gettext realpath transfig texinfo curl librsvg2-bin xsltproc vorbis-tools swig quilt faketime optipng python-gnupg python3-gnupg}.each do |pkg|
+%w{ant ant-contrib autoconf autopoint bison cmake expect libtool libsaxonb-java libssl1.0.0 libssl-dev maven openjdk-7-jdk javacc python python-magic git-core mercurial subversion bzr git-svn make perlmagick pkg-config zip yasm inkscape imagemagick gettext realpath transfig texinfo curl librsvg2-bin xsltproc vorbis-tools swig quilt faketime optipng python-gnupg python3-gnupg nasm}.each do |pkg|
   package pkg do
     action :install
   end
index 44d73e01ee95724e6364a634a296aafcd8bb642c..f1c6f7b94dc0b672041a83c890d8c98005b45259 100644 (file)
@@ -9,7 +9,7 @@
 # Override the path to the Android NDK, $ANDROID_NDK by default
 # ndk_path = "/path/to/android-ndk"
 # Build tools version to be used
-build_tools = "21.1.2"
+build_tools = "21.1.2"
 
 # Command for running Ant
 # ant = "/path/to/ant"
index 9c8acfa2ab846ad9e0354d87c00343aff6d58520..498cd4139464ca29160006e2628bba552bcb7949 100644 (file)
@@ -35,7 +35,7 @@ import logging
 
 import common
 import metadata
-from common import FDroidException, BuildException, VCSException, FDroidPopen, SilentPopen
+from common import FDroidException, BuildException, VCSException, FDroidPopen, SdkToolsPopen
 
 try:
     import paramiko
@@ -754,7 +754,7 @@ def build_local(app, thisbuild, vcs, build_dir, output_dir, srclib_dir, extlib_d
     if not os.path.exists(src):
         raise BuildException("Unsigned apk is not at expected location of " + src)
 
-    p = SilentPopen([config['aapt'], 'dump', 'badging', src])
+    p = SdkToolsPopen(['aapt', 'dump', 'badging', src])
 
     vercode = None
     version = None
index 3a42ec58d0b8e87704dce316f1a2860fedeec397..72208f5baee9a5195da655568fae36496e10da00 100644 (file)
@@ -130,38 +130,6 @@ def read_config(opts, config_file='config.py'):
 
     fill_config_defaults(config)
 
-    if not test_sdk_exists(config):
-        sys.exit(3)
-
-    if not test_build_tools_exists(config):
-        sys.exit(3)
-
-    bin_paths = {
-        'aapt': [
-            os.path.join(config['sdk_path'], 'build-tools', config['build_tools'], 'aapt'),
-            ],
-        'zipalign': [
-            os.path.join(config['sdk_path'], 'tools', 'zipalign'),
-            os.path.join(config['sdk_path'], 'build-tools', config['build_tools'], 'zipalign'),
-            ],
-        'android': [
-            os.path.join(config['sdk_path'], 'tools', 'android'),
-            ],
-        'adb': [
-            os.path.join(config['sdk_path'], 'platform-tools', 'adb'),
-            ],
-        }
-
-    for b, paths in bin_paths.items():
-        config[b] = None
-        for path in paths:
-            if os.path.isfile(path):
-                config[b] = path
-                break
-        if config[b] is None:
-            logging.warn("Could not find %s in any of the following paths:\n%s" % (
-                b, '\n'.join(paths)))
-
     # There is no standard, so just set up the most common environment
     # variables
     env = os.environ
@@ -197,7 +165,45 @@ def read_config(opts, config_file='config.py'):
     return config
 
 
+def find_sdk_tools_cmd(cmd):
+    '''find a working path to a tool from the Android SDK'''
+
+    tooldirs = []
+    if config is not None and 'sdk_path' in config and os.path.exists(config['sdk_path']):
+        # try to find a working path to this command, in all the recent possible paths
+        if 'build_tools' in config:
+            build_tools = os.path.join(config['sdk_path'], 'build-tools')
+            # if 'build_tools' was manually set and exists, check only that one
+            configed_build_tools = os.path.join(build_tools, config['build_tools'])
+            if os.path.exists(configed_build_tools):
+                tooldirs.append(configed_build_tools)
+            else:
+                # no configed version, so hunt known paths for it
+                for f in sorted(os.listdir(build_tools), reverse=True):
+                    if os.path.isdir(os.path.join(build_tools, f)):
+                        tooldirs.append(os.path.join(build_tools, f))
+                tooldirs.append(build_tools)
+        sdk_tools = os.path.join(config['sdk_path'], 'tools')
+        if os.path.exists(sdk_tools):
+            tooldirs.append(sdk_tools)
+        sdk_platform_tools = os.path.join(config['sdk_path'], 'platform-tools')
+        if os.path.exists(sdk_platform_tools):
+            tooldirs.append(sdk_platform_tools)
+    tooldirs.append('/usr/bin')
+    for d in tooldirs:
+        if os.path.isfile(os.path.join(d, cmd)):
+            return os.path.join(d, cmd)
+    # did not find the command, exit with error message
+    ensure_build_tools_exists(config)
+
+
 def test_sdk_exists(thisconfig):
+    if 'sdk_path' not in thisconfig:
+        if 'aapt' in thisconfig and os.path.isfile(thisconfig['aapt']):
+            return True
+        else:
+            logging.error("'sdk_path' not set in config.py!")
+            return False
     if thisconfig['sdk_path'] == default_config['sdk_path']:
         logging.error('No Android SDK found!')
         logging.error('You can use ANDROID_HOME to set the path to your SDK, i.e.:')
@@ -217,16 +223,15 @@ def test_sdk_exists(thisconfig):
     return True
 
 
-def test_build_tools_exists(thisconfig):
+def ensure_build_tools_exists(thisconfig):
     if not test_sdk_exists(thisconfig):
-        return False
+        sys.exit(3)
     build_tools = os.path.join(thisconfig['sdk_path'], 'build-tools')
     versioned_build_tools = os.path.join(build_tools, thisconfig['build_tools'])
     if not os.path.isdir(versioned_build_tools):
         logging.critical('Android Build Tools path "'
                          + versioned_build_tools + '" does not exist!')
-        return False
-    return True
+        sys.exit(3)
 
 
 def write_password_file(pwtype, password=None):
@@ -496,7 +501,7 @@ class vcs_git(vcs):
     # fdroidserver) and then we'll proceed to destroy it! This is called as
     # a safety check.
     def checkrepo(self):
-        p = SilentPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local)
+        p = FDroidPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local, output=False)
         result = p.output.rstrip()
         if not result.endswith(self.local):
             raise VCSException('Repository mismatch')
@@ -512,12 +517,12 @@ class vcs_git(vcs):
         else:
             self.checkrepo()
             # Discard any working tree changes
-            p = SilentPopen(['git', 'reset', '--hard'], cwd=self.local)
+            p = FDroidPopen(['git', 'reset', '--hard'], cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("Git reset failed", p.output)
             # Remove untracked files now, in case they're tracked in the target
             # revision (it happens!)
-            p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
+            p = FDroidPopen(['git', 'clean', '-dffx'], cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("Git clean failed", p.output)
             if not self.refreshed:
@@ -525,28 +530,28 @@ class vcs_git(vcs):
                 p = FDroidPopen(['git', 'fetch', 'origin'], cwd=self.local)
                 if p.returncode != 0:
                     raise VCSException("Git fetch failed", p.output)
-                p = SilentPopen(['git', 'fetch', '--prune', '--tags', 'origin'], cwd=self.local)
+                p = FDroidPopen(['git', 'fetch', '--prune', '--tags', 'origin'], cwd=self.local, output=False)
                 if p.returncode != 0:
                     raise VCSException("Git fetch failed", p.output)
                 # Recreate origin/HEAD as git clone would do it, in case it disappeared
-                p = SilentPopen(['git', 'remote', 'set-head', 'origin', '--auto'], cwd=self.local)
+                p = FDroidPopen(['git', 'remote', 'set-head', 'origin', '--auto'], cwd=self.local, output=False)
                 if p.returncode != 0:
                     lines = p.output.splitlines()
                     if 'Multiple remote HEAD branches' not in lines[0]:
                         raise VCSException("Git remote set-head failed", p.output)
                     branch = lines[1].split(' ')[-1]
-                    p2 = SilentPopen(['git', 'remote', 'set-head', 'origin', branch], cwd=self.local)
+                    p2 = FDroidPopen(['git', 'remote', 'set-head', 'origin', branch], cwd=self.local, output=False)
                     if p2.returncode != 0:
                         raise VCSException("Git remote set-head failed", p.output + '\n' + p2.output)
                 self.refreshed = True
         # origin/HEAD is the HEAD of the remote, e.g. the "default branch" on
         # a github repo. Most of the time this is the same as origin/master.
         rev = rev or 'origin/HEAD'
-        p = SilentPopen(['git', 'checkout', '-f', rev], cwd=self.local)
+        p = FDroidPopen(['git', 'checkout', '-f', rev], cwd=self.local, output=False)
         if p.returncode != 0:
             raise VCSException("Git checkout of '%s' failed" % rev, p.output)
         # Get rid of any uncontrolled files left behind
-        p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
+        p = FDroidPopen(['git', 'clean', '-dffx'], cwd=self.local, output=False)
         if p.returncode != 0:
             raise VCSException("Git clean failed", p.output)
 
@@ -569,10 +574,10 @@ class vcs_git(vcs):
                 ['git', 'reset', '--hard'],
                 ['git', 'clean', '-dffx'],
                 ]:
-            p = SilentPopen(['git', 'submodule', 'foreach', '--recursive'] + cmd, cwd=self.local)
+            p = FDroidPopen(['git', 'submodule', 'foreach', '--recursive'] + cmd, cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("Git submodule reset failed", p.output)
-        p = SilentPopen(['git', 'submodule', 'sync'], cwd=self.local)
+        p = FDroidPopen(['git', 'submodule', 'sync'], cwd=self.local, output=False)
         if p.returncode != 0:
             raise VCSException("Git submodule sync failed", p.output)
         p = FDroidPopen(['git', 'submodule', 'update', '--init', '--force', '--recursive'], cwd=self.local)
@@ -581,15 +586,15 @@ class vcs_git(vcs):
 
     def gettags(self):
         self.checkrepo()
-        p = SilentPopen(['git', 'tag'], cwd=self.local)
+        p = FDroidPopen(['git', 'tag'], cwd=self.local, output=False)
         return p.output.splitlines()
 
     def latesttags(self, alltags, number):
         self.checkrepo()
-        p = SilentPopen(['echo "' + '\n'.join(alltags) + '" | '
+        p = FDroidPopen(['echo "' + '\n'.join(alltags) + '" | '
                         + 'xargs -I@ git log --format=format:"%at @%n" -1 @ | '
                         + 'sort -n | awk \'{print $2}\''],
-                        cwd=self.local, shell=True)
+                        cwd=self.local, shell=True, output=False)
         return p.output.splitlines()[-number:]
 
 
@@ -610,7 +615,7 @@ class vcs_gitsvn(vcs):
     # fdroidserver) and then we'll proceed to destory it! This is called as
     # a safety check.
     def checkrepo(self):
-        p = SilentPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local)
+        p = FDroidPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local, output=False)
         result = p.output.rstrip()
         if not result.endswith(self.local):
             raise VCSException('Repository mismatch')
@@ -628,12 +633,12 @@ class vcs_gitsvn(vcs):
                         gitsvn_cmd += ' -t %s' % i[5:]
                     elif i.startswith('branches='):
                         gitsvn_cmd += ' -b %s' % i[9:]
-                p = SilentPopen([gitsvn_cmd + " %s %s" % (remote_split[0], self.local)], shell=True)
+                p = FDroidPopen([gitsvn_cmd + " %s %s" % (remote_split[0], self.local)], shell=True, output=False)
                 if p.returncode != 0:
                     self.clone_failed = True
                     raise VCSException("Git svn clone failed", p.output)
             else:
-                p = SilentPopen([gitsvn_cmd + " %s %s" % (self.remote, self.local)], shell=True)
+                p = FDroidPopen([gitsvn_cmd + " %s %s" % (self.remote, self.local)], shell=True, output=False)
                 if p.returncode != 0:
                     self.clone_failed = True
                     raise VCSException("Git svn clone failed", p.output)
@@ -641,20 +646,20 @@ class vcs_gitsvn(vcs):
         else:
             self.checkrepo()
             # Discard any working tree changes
-            p = SilentPopen(['git', 'reset', '--hard'], cwd=self.local)
+            p = FDroidPopen(['git', 'reset', '--hard'], cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("Git reset failed", p.output)
             # Remove untracked files now, in case they're tracked in the target
             # revision (it happens!)
-            p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
+            p = FDroidPopen(['git', 'clean', '-dffx'], cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("Git clean failed", p.output)
             if not self.refreshed:
                 # Get new commits, branches and tags from repo
-                p = SilentPopen(['%sgit svn fetch %s' % self.userargs()], cwd=self.local, shell=True)
+                p = FDroidPopen(['%sgit svn fetch %s' % self.userargs()], cwd=self.local, shell=True, output=False)
                 if p.returncode != 0:
                     raise VCSException("Git svn fetch failed")
-                p = SilentPopen(['%sgit svn rebase %s' % self.userargs()], cwd=self.local, shell=True)
+                p = FDroidPopen(['%sgit svn rebase %s' % self.userargs()], cwd=self.local, shell=True, output=False)
                 if p.returncode != 0:
                     raise VCSException("Git svn rebase failed", p.output)
                 self.refreshed = True
@@ -664,8 +669,7 @@ class vcs_gitsvn(vcs):
             nospaces_rev = rev.replace(' ', '%20')
             # Try finding a svn tag
             for treeish in ['origin/', '']:
-                p = SilentPopen(['git', 'checkout', treeish + 'tags/' + nospaces_rev],
-                                cwd=self.local)
+                p = FDroidPopen(['git', 'checkout', treeish + 'tags/' + nospaces_rev], cwd=self.local, output=False)
                 if p.returncode == 0:
                     break
             if p.returncode != 0:
@@ -686,8 +690,7 @@ class vcs_gitsvn(vcs):
 
                     svn_rev = svn_rev if svn_rev[0] == 'r' else 'r' + svn_rev
 
-                    p = SilentPopen(['git', 'svn', 'find-rev', '--before', svn_rev, treeish],
-                                    cwd=self.local)
+                    p = FDroidPopen(['git', 'svn', 'find-rev', '--before', svn_rev, treeish], cwd=self.local, output=False)
                     git_rev = p.output.rstrip()
 
                     if p.returncode == 0 and git_rev:
@@ -695,17 +698,17 @@ class vcs_gitsvn(vcs):
 
                 if p.returncode != 0 or not git_rev:
                     # Try a plain git checkout as a last resort
-                    p = SilentPopen(['git', 'checkout', rev], cwd=self.local)
+                    p = FDroidPopen(['git', 'checkout', rev], cwd=self.local, output=False)
                     if p.returncode != 0:
                         raise VCSException("No git treeish found and direct git checkout of '%s' failed" % rev, p.output)
                 else:
                     # Check out the git rev equivalent to the svn rev
-                    p = SilentPopen(['git', 'checkout', git_rev], cwd=self.local)
+                    p = FDroidPopen(['git', 'checkout', git_rev], cwd=self.local, output=False)
                     if p.returncode != 0:
                         raise VCSException("Git checkout of '%s' failed" % rev, p.output)
 
         # Get rid of any uncontrolled files left behind
-        p = SilentPopen(['git', 'clean', '-dffx'], cwd=self.local)
+        p = FDroidPopen(['git', 'clean', '-dffx'], cwd=self.local, output=False)
         if p.returncode != 0:
             raise VCSException("Git clean failed", p.output)
 
@@ -718,7 +721,7 @@ class vcs_gitsvn(vcs):
 
     def getref(self):
         self.checkrepo()
-        p = SilentPopen(['git', 'svn', 'find-rev', 'HEAD'], cwd=self.local)
+        p = FDroidPopen(['git', 'svn', 'find-rev', 'HEAD'], cwd=self.local, output=False)
         if p.returncode != 0:
             return None
         return p.output.strip()
@@ -731,16 +734,16 @@ class vcs_hg(vcs):
 
     def gotorevisionx(self, rev):
         if not os.path.exists(self.local):
-            p = SilentPopen(['hg', 'clone', self.remote, self.local])
+            p = FDroidPopen(['hg', 'clone', self.remote, self.local], output=False)
             if p.returncode != 0:
                 self.clone_failed = True
                 raise VCSException("Hg clone failed", p.output)
         else:
-            p = SilentPopen(['hg status -uS | xargs rm -rf'], cwd=self.local, shell=True)
+            p = FDroidPopen(['hg status -uS | xargs rm -rf'], cwd=self.local, shell=True, output=False)
             if p.returncode != 0:
                 raise VCSException("Hg clean failed", p.output)
             if not self.refreshed:
-                p = SilentPopen(['hg', 'pull'], cwd=self.local)
+                p = FDroidPopen(['hg', 'pull'], cwd=self.local, output=False)
                 if p.returncode != 0:
                     raise VCSException("Hg pull failed", p.output)
                 self.refreshed = True
@@ -748,22 +751,22 @@ class vcs_hg(vcs):
         rev = rev or 'default'
         if not rev:
             return
-        p = SilentPopen(['hg', 'update', '-C', rev], cwd=self.local)
+        p = FDroidPopen(['hg', 'update', '-C', rev], cwd=self.local, output=False)
         if p.returncode != 0:
             raise VCSException("Hg checkout of '%s' failed" % rev, p.output)
-        p = SilentPopen(['hg', 'purge', '--all'], cwd=self.local)
+        p = FDroidPopen(['hg', 'purge', '--all'], cwd=self.local, output=False)
         # Also delete untracked files, we have to enable purge extension for that:
         if "'purge' is provided by the following extension" in p.output:
             with open(os.path.join(self.local, '.hg', 'hgrc'), "a") as myfile:
                 myfile.write("\n[extensions]\nhgext.purge=\n")
-            p = SilentPopen(['hg', 'purge', '--all'], cwd=self.local)
+            p = FDroidPopen(['hg', 'purge', '--all'], cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("HG purge failed", p.output)
         elif p.returncode != 0:
             raise VCSException("HG purge failed", p.output)
 
     def gettags(self):
-        p = SilentPopen(['hg', 'tags', '-q'], cwd=self.local)
+        p = FDroidPopen(['hg', 'tags', '-q'], cwd=self.local, output=False)
         return p.output.splitlines()[1:]
 
 
@@ -774,27 +777,27 @@ class vcs_bzr(vcs):
 
     def gotorevisionx(self, rev):
         if not os.path.exists(self.local):
-            p = SilentPopen(['bzr', 'branch', self.remote, self.local])
+            p = FDroidPopen(['bzr', 'branch', self.remote, self.local], output=False)
             if p.returncode != 0:
                 self.clone_failed = True
                 raise VCSException("Bzr branch failed", p.output)
         else:
-            p = SilentPopen(['bzr', 'clean-tree', '--force', '--unknown', '--ignored'], cwd=self.local)
+            p = FDroidPopen(['bzr', 'clean-tree', '--force', '--unknown', '--ignored'], cwd=self.local, output=False)
             if p.returncode != 0:
                 raise VCSException("Bzr revert failed", p.output)
             if not self.refreshed:
-                p = SilentPopen(['bzr', 'pull'], cwd=self.local)
+                p = FDroidPopen(['bzr', 'pull'], cwd=self.local, output=False)
                 if p.returncode != 0:
                     raise VCSException("Bzr update failed", p.output)
                 self.refreshed = True
 
         revargs = list(['-r', rev] if rev else [])
-        p = SilentPopen(['bzr', 'revert'] + revargs, cwd=self.local)
+        p = FDroidPopen(['bzr', 'revert'] + revargs, cwd=self.local, output=False)
         if p.returncode != 0:
             raise VCSException("Bzr revert of '%s' failed" % rev, p.output)
 
     def gettags(self):
-        p = SilentPopen(['bzr', 'tags'], cwd=self.local)
+        p = FDroidPopen(['bzr', 'tags'], cwd=self.local, output=False)
         return [tag.split('   ')[0].strip() for tag in
                 p.output.splitlines()]
 
@@ -919,7 +922,7 @@ def remove_debuggable_flags(root_dir):
     for root, dirs, files in os.walk(root_dir):
         if 'AndroidManifest.xml' in files:
             path = os.path.join(root, 'AndroidManifest.xml')
-            p = SilentPopen(['sed', '-i', 's/android:debuggable="[^"]*"//g', path])
+            p = FDroidPopen(['sed', '-i', 's/android:debuggable="[^"]*"//g', path], output=False)
             if p.returncode != 0:
                 raise BuildException("Failed to remove debuggable flags of %s" % path)
 
@@ -1253,10 +1256,9 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
 
         if build['target']:
             n = build["target"].split('-')[1]
-            SilentPopen(['sed', '-i',
+            FDroidPopen(['sed', '-i',
                          's@compileSdkVersion *[0-9]*@compileSdkVersion ' + n + '@g',
-                         'build.gradle'],
-                        cwd=root_dir)
+                         'build.gradle'], cwd=root_dir, output=False)
 
     # Remove forced debuggable flags
     remove_debuggable_flags(root_dir)
@@ -1268,17 +1270,15 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             if not os.path.isfile(path):
                 continue
             if has_extension(path, 'xml'):
-                p = SilentPopen(['sed', '-i',
-                                 's/android:versionName="[^"]*"/android:versionName="'
-                                 + build['version'] + '"/g',
-                                 path])
+                p = FDroidPopen(['sed', '-i',
+                                 's/android:versionName="[^"]*"/android:versionName="' + build['version'] + '"/g',
+                                 path], output=False)
                 if p.returncode != 0:
                     raise BuildException("Failed to amend manifest")
             elif has_extension(path, 'gradle'):
-                p = SilentPopen(['sed', '-i',
-                                 's/versionName *=* *"[^"]*"/versionName = "'
-                                 + build['version'] + '"/g',
-                                 path])
+                p = FDroidPopen(['sed', '-i',
+                                 's/versionName *=* *"[^"]*"/versionName = "' + build['version'] + '"/g',
+                                 path], output=False)
                 if p.returncode != 0:
                     raise BuildException("Failed to amend build.gradle")
     if build['forcevercode']:
@@ -1287,17 +1287,15 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             if not os.path.isfile(path):
                 continue
             if has_extension(path, 'xml'):
-                p = SilentPopen(['sed', '-i',
-                                 's/android:versionCode="[^"]*"/android:versionCode="'
-                                 + build['vercode'] + '"/g',
-                                 path])
+                p = FDroidPopen(['sed', '-i',
+                                 's/android:versionCode="[^"]*"/android:versionCode="' + build['vercode'] + '"/g',
+                                 path], output=False)
                 if p.returncode != 0:
                     raise BuildException("Failed to amend manifest")
             elif has_extension(path, 'gradle'):
-                p = SilentPopen(['sed', '-i',
-                                 's/versionCode *=* *[0-9]*/versionCode = '
-                                 + build['vercode'] + '/g',
-                                 path])
+                p = FDroidPopen(['sed', '-i',
+                                 's/versionCode *=* *[0-9]*/versionCode = ' + build['vercode'] + '/g',
+                                 path], output=False)
                 if p.returncode != 0:
                     raise BuildException("Failed to amend build.gradle")
 
@@ -1309,9 +1307,9 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             logging.info("Removing {0}".format(part))
             if os.path.lexists(dest):
                 if os.path.islink(dest):
-                    SilentPopen(['unlink ' + dest], shell=True)
+                    FDroidPopen(['unlink ' + dest], shell=True, output=False)
                 else:
-                    SilentPopen(['rm -rf ' + dest], shell=True)
+                    FDroidPopen(['rm -rf ' + dest], shell=True, output=False)
             else:
                 logging.info("...but it didn't exist")
 
@@ -1350,8 +1348,8 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
 
     # Generate (or update) the ant build file, build.xml...
     if build['update'] and build['update'] != ['no'] and build['type'] == 'ant':
-        parms = [config['android'], 'update', 'lib-project']
-        lparms = [config['android'], 'update', 'project']
+        parms = ['android', 'update', 'lib-project']
+        lparms = ['android', 'update', 'project']
 
         if build['target']:
             parms += ['-t', build['target']]
@@ -1369,7 +1367,7 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             else:
                 logging.debug("Updating subproject %s" % d)
                 cmd = lparms + ['-p', d]
-            p = FDroidPopen(cmd, cwd=root_dir)
+            p = SdkToolsPopen(cmd, cwd=root_dir)
             # Check to see whether an error was returned without a proper exit
             # code (this is the case for the 'no target set or target invalid'
             # error)
@@ -1600,9 +1598,7 @@ def isApkDebuggable(apkfile, config):
 
     :param apkfile: full path to the apk to check"""
 
-    p = SilentPopen([os.path.join(config['sdk_path'], 'build-tools',
-                                  config['build_tools'], 'aapt'),
-                     'dump', 'xmltree', apkfile, 'AndroidManifest.xml'])
+    p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'])
     if p.returncode != 0:
         logging.critical("Failed to get apk manifest information")
         sys.exit(1)
@@ -1641,8 +1637,12 @@ class PopenResult:
     output = ''
 
 
-def SilentPopen(commands, cwd=None, shell=False):
-    return FDroidPopen(commands, cwd=cwd, shell=shell, output=False)
+def SdkToolsPopen(commands, cwd=None, shell=False, output=True):
+    cmd = commands[0]
+    if cmd not in config:
+        config[cmd] = find_sdk_tools_cmd(commands[0])
+    return FDroidPopen([config[cmd]] + commands[1:],
+                       cwd=cwd, shell=shell, output=output)
 
 
 def FDroidPopen(commands, cwd=None, shell=False, output=True):
@@ -1696,6 +1696,7 @@ def remove_signing_keys(build_dir):
         re.compile(r'^[\t ]*signingConfig [^ ]*$'),
         re.compile(r'.*android\.signingConfigs\.[^{]*$'),
         re.compile(r'.*variant\.outputFile = .*'),
+        re.compile(r'.*output\.outputFile = .*'),
         re.compile(r'.*\.readLine\(.*'),
         ]
     for root, dirs, files in os.walk(build_dir):
index 6f96a24b29843f3187a7a518382b107ed2f76416..be62f103a80799c967d2b29510d910f4a437cde1 100644 (file)
@@ -124,6 +124,7 @@ def main():
         prefix = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
         examplesdir = prefix + '/examples'
 
+    aapt = None
     fdroiddir = os.getcwd()
     test_config = dict()
     common.fill_config_defaults(test_config)
@@ -133,21 +134,28 @@ def main():
     if options.android_home is not None:
         test_config['sdk_path'] = options.android_home
     elif not common.test_sdk_exists(test_config):
-        # if neither --android-home nor the default sdk_path exist, prompt the user
-        default_sdk_path = '/opt/android-sdk'
-        while not options.no_prompt:
-            try:
-                s = raw_input('Enter the path to the Android SDK ('
-                              + default_sdk_path + ') here:\n> ')
-            except KeyboardInterrupt:
-                print('')
-                sys.exit(1)
-            if re.match('^\s*$', s) is not None:
-                test_config['sdk_path'] = default_sdk_path
-            else:
-                test_config['sdk_path'] = s
-            if common.test_sdk_exists(test_config):
-                break
+        if os.path.isfile('/usr/bin/aapt'):
+            # remove sdk_path and build_tools, they are not required
+            test_config.pop('sdk_path', None)
+            test_config.pop('build_tools', None)
+            # make sure at least aapt is found, since this can't do anything without it
+            test_config['aapt'] = common.find_sdk_tools_cmd('aapt')
+        else:
+            # if neither --android-home nor the default sdk_path exist, prompt the user
+            default_sdk_path = '/opt/android-sdk'
+            while not options.no_prompt:
+                try:
+                    s = raw_input('Enter the path to the Android SDK ('
+                                  + default_sdk_path + ') here:\n> ')
+                except KeyboardInterrupt:
+                    print('')
+                    sys.exit(1)
+                if re.match('^\s*$', s) is not None:
+                    test_config['sdk_path'] = default_sdk_path
+                else:
+                    test_config['sdk_path'] = s
+                if common.test_sdk_exists(test_config):
+                    break
     if not common.test_sdk_exists(test_config):
         sys.exit(3)
 
@@ -162,34 +170,35 @@ def main():
         # "$ANDROID_HOME" may be used if the env var is set up correctly.
         # If android_home is not None, the path given from the command line
         # will be directly written in the config.
-        write_to_config(test_config, 'sdk_path', options.android_home)
+        if 'sdk_path' in test_config:
+            write_to_config(test_config, 'sdk_path', options.android_home)
     else:
         logging.warn('Looks like this is already an F-Droid repo, cowardly refusing to overwrite it...')
         logging.info('Try running `fdroid init` in an empty directory.')
         sys.exit()
 
-    # try to find a working aapt, in all the recent possible paths
-    build_tools = os.path.join(test_config['sdk_path'], 'build-tools')
-    aaptdirs = []
-    aaptdirs.append(os.path.join(build_tools, test_config['build_tools']))
-    aaptdirs.append(build_tools)
-    for f in os.listdir(build_tools):
-        if os.path.isdir(os.path.join(build_tools, f)):
-            aaptdirs.append(os.path.join(build_tools, f))
-    for d in sorted(aaptdirs, reverse=True):
-        if os.path.isfile(os.path.join(d, 'aapt')):
-            aapt = os.path.join(d, 'aapt')
-            break
-    if os.path.isfile(aapt):
-        dirname = os.path.basename(os.path.dirname(aapt))
-        if dirname == 'build-tools':
-            # this is the old layout, before versioned build-tools
-            test_config['build_tools'] = ''
-        else:
-            test_config['build_tools'] = dirname
-        write_to_config(test_config, 'build_tools')
-    if not common.test_build_tools_exists(test_config):
-        sys.exit(3)
+    if 'aapt' not in test_config or not os.path.isfile(test_config['aapt']):
+        # try to find a working aapt, in all the recent possible paths
+        build_tools = os.path.join(test_config['sdk_path'], 'build-tools')
+        aaptdirs = []
+        aaptdirs.append(os.path.join(build_tools, test_config['build_tools']))
+        aaptdirs.append(build_tools)
+        for f in os.listdir(build_tools):
+            if os.path.isdir(os.path.join(build_tools, f)):
+                aaptdirs.append(os.path.join(build_tools, f))
+        for d in sorted(aaptdirs, reverse=True):
+            if os.path.isfile(os.path.join(d, 'aapt')):
+                aapt = os.path.join(d, 'aapt')
+                break
+        if os.path.isfile(aapt):
+            dirname = os.path.basename(os.path.dirname(aapt))
+            if dirname == 'build-tools':
+                # this is the old layout, before versioned build-tools
+                test_config['build_tools'] = ''
+            else:
+                test_config['build_tools'] = dirname
+            write_to_config(test_config, 'build_tools')
+        common.ensure_build_tools_exists(test_config)
 
     # now that we have a local config.py, read configuration...
     config = common.read_config(options)
@@ -275,7 +284,8 @@ def main():
     logging.info('Built repo based in "' + fdroiddir + '"')
     logging.info('with this config:')
     logging.info('  Android SDK:\t\t\t' + config['sdk_path'])
-    logging.info('  Android SDK Build Tools:\t' + os.path.dirname(aapt))
+    if aapt:
+        logging.info('  Android SDK Build Tools:\t' + os.path.dirname(aapt))
     logging.info('  Android NDK (optional):\t' + ndk_path)
     logging.info('  Keystore for signing key:\t' + keystore)
     if repo_keyalias is not None:
index f6862e5a17703d83e14bc67f01244244722d9bef..a5cb98adf17c0fa0cb63cb222be171e4c325c9b3 100644 (file)
@@ -25,14 +25,14 @@ from optparse import OptionParser, OptionError
 import logging
 
 import common
-from common import FDroidPopen, FDroidException
+from common import SdkToolsPopen, FDroidException
 
 options = None
 config = None
 
 
 def devices():
-    p = FDroidPopen([config['adb'], "devices"])
+    p = SdkToolsPopen(['adb', "devices"])
     if p.returncode != 0:
         raise FDroidException("An error occured when finding devices: %s" % p.output)
     lines = p.output.splitlines()
@@ -103,7 +103,7 @@ def main():
         logging.info("Installing %s..." % apk)
         for dev in devs:
             logging.info("Installing %s on %s..." % (apk, dev))
-            p = FDroidPopen([config['adb'], "-s", dev, "install", apk])
+            p = SdkToolsPopen(['adb', "-s", dev, "install", apk])
             fail = ""
             for line in p.output.splitlines():
                 if line.startswith("Failure"):
index bfacc2da93c50426ddc0b020de5bd518884bcbb4..f6deefbb05f9df497264270b1b9569121928806f 100644 (file)
@@ -165,23 +165,29 @@ def main():
     apps = common.read_app_args(args, allapps, False)
 
     for appid, app in apps.iteritems():
-        curid = appid
-        lastcommit = ''
-
         if app['Disabled']:
             continue
 
+        curid = appid
         count['app_total'] += 1
 
+        curbuild = None
         for build in app['builds']:
-            if build['commit'] and not build['disable']:
-                lastcommit = build['commit']
+            if not curbuild or int(build['vercode']) > int(curbuild['vercode']):
+                curbuild = build
 
         # Potentially incorrect UCM
-        if (app['Update Check Mode'] == 'RepoManifest' and
-                any(s in lastcommit for s in '.,_-/')):
+        if (curbuild and curbuild['commit']
+                and app['Update Check Mode'] == 'RepoManifest' and
+                any(s in curbuild['commit'] for s in '.,_-/')):
             pwarn("Last used commit '%s' looks like a tag, but Update Check Mode is '%s'" % (
-                lastcommit, app['Update Check Mode']))
+                curbuild['commit'], app['Update Check Mode']))
+
+        # Dangerous auto updates
+        if curbuild and app['Auto Update Mode'] != 'None':
+            for flag in ['target', 'srclibs', 'scanignore']:
+                if curbuild[flag]:
+                    pwarn("Auto Update Mode is enabled but '%s' is manually set at '%s'" % (flag, curbuild[flag]))
 
         # Summary size limit
         summ_chars = len(app['Summary'])
index 39f601c4f057ccc14fac25f5b4b51a8f996b5ee1..48d0503ef43d756b1d54fba499fa38b03b0ad9f8 100644 (file)
@@ -28,7 +28,7 @@ import logging
 
 import common
 import metadata
-from common import FDroidPopen, BuildException
+from common import FDroidPopen, SdkToolsPopen, BuildException
 
 config = None
 options = None
@@ -213,8 +213,8 @@ def main():
                 raise BuildException("Failed to sign application")
 
             # Zipalign it...
-            p = FDroidPopen([config['zipalign'], '-v', '4', apkfile,
-                             os.path.join(output_dir, apkfilename)])
+            p = SdkToolsPopen(['zipalign', '-v', '4', apkfile,
+                               os.path.join(output_dir, apkfilename)])
             if p.returncode != 0:
                 raise BuildException("Failed to align application")
             os.remove(apkfile)
index 0339f4606fb1c4119d4cc53db3e48bf155be4e9c..42806ce5b3f411132d5037d473c88c17c99cbb4a 100644 (file)
@@ -39,7 +39,7 @@ import logging
 
 import common
 import metadata
-from common import FDroidPopen, SilentPopen
+from common import FDroidPopen, SdkToolsPopen
 from metadata import MetaDataException
 
 
@@ -436,7 +436,7 @@ def scan_apks(apps, apkcache, repodir, knownapks):
             thisinfo['features'] = set()
             thisinfo['icons_src'] = {}
             thisinfo['icons'] = {}
-            p = SilentPopen([config['aapt'], 'dump', 'badging', apkfile])
+            p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile])
             if p.returncode != 0:
                 if options.delete_unknown:
                     if os.path.exists(apkfile):
@@ -868,7 +868,7 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
         if current_version_file is not None \
                 and config['make_current_version_link'] \
                 and repodir == 'repo':  # only create these
-            sanitized_name = re.sub('''[ '"&%?+=]''', '',
+            sanitized_name = re.sub('''[ '"&%?+=/]''', '',
                                     app[config['current_version_name_source']])
             apklinkname = sanitized_name + '.apk'
             current_version_path = os.path.join(repodir, current_version_file)
diff --git a/tests/common.TestCase b/tests/common.TestCase
new file mode 100755 (executable)
index 0000000..d206637
--- /dev/null
@@ -0,0 +1,95 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
+
+import inspect
+import optparse
+import os
+import sys
+import unittest
+
+localmodule = os.path.realpath(os.path.join(
+        os.path.dirname(inspect.getfile(inspect.currentframe())),
+        '..'))
+print('localmodule: ' + localmodule)
+if localmodule not in sys.path:
+    sys.path.insert(0,localmodule)
+
+import fdroidserver.common
+
+class CommonTest(unittest.TestCase):
+    '''fdroidserver/common.py'''
+
+    def _set_build_tools(self):
+        build_tools = os.path.join(fdroidserver.common.config['sdk_path'], 'build-tools')
+        if os.path.exists(build_tools):
+            fdroidserver.common.config['build_tools'] = ''
+            for f in sorted(os.listdir(build_tools), reverse=True):
+                versioned = os.path.join(build_tools, f)
+                if os.path.isdir(versioned) \
+                        and os.path.isfile(os.path.join(versioned, 'aapt')):
+                    fdroidserver.common.config['build_tools'] = versioned
+                    break
+            return True
+        else:
+            print 'no build-tools found: ' + build_tools
+            return False
+
+    def _find_all(self):
+        for cmd in ('aapt', 'adb', 'android', 'zipalign'):
+            path = fdroidserver.common.find_sdk_tools_cmd(cmd)
+            if path is not None:
+                self.assertTrue(os.path.exists(path))
+                self.assertTrue(os.path.isfile(path))
+
+    def test_find_sdk_tools_cmd(self):
+        fdroidserver.common.config = dict()
+        # TODO add this once everything works without sdk_path set in config
+        #self._find_all()
+        sdk_path = os.getenv('ANDROID_HOME')
+        if os.path.exists(sdk_path):
+            fdroidserver.common.config['sdk_path'] = sdk_path
+            if os.path.exists('/usr/bin/aapt'):
+                # this test only works when /usr/bin/aapt is installed
+                self._find_all()
+            build_tools = os.path.join(sdk_path, 'build-tools')
+            if self._set_build_tools():
+                self._find_all()
+            else:
+                print 'no build-tools found: ' + build_tools
+
+    def testIsApkDebuggable(self):
+        config = dict()
+        config['sdk_path'] = os.getenv('ANDROID_HOME')
+        fdroidserver.common.config = config
+        self._set_build_tools();
+        config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
+        # these are set debuggable
+        testfiles = []
+        testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip.apk'))
+        testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-badsig.apk'))
+        testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-badcert.apk'))
+        for apkfile in testfiles:
+            debuggable = fdroidserver.common.isApkDebuggable(apkfile, config)
+            self.assertTrue(debuggable,
+                            "debuggable APK state was not properly parsed!")
+        # these are set NOT debuggable
+        testfiles = []
+        testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-release.apk'))
+        testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-release-unsigned.apk'))
+        for apkfile in testfiles:
+            debuggable = fdroidserver.common.isApkDebuggable(apkfile, config)
+            self.assertFalse(debuggable,
+                             "debuggable APK state was not properly parsed!")
+
+
+if __name__ == "__main__":
+    parser = optparse.OptionParser()
+    parser.add_option("-v", "--verbose", action="store_true", default=False,
+                      help="Spew out even more information than normal")
+    (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
+
+    newSuite = unittest.TestSuite()
+    newSuite.addTest(unittest.makeSuite(CommonTest))
+    unittest.main()
diff --git a/tests/install.TestCase b/tests/install.TestCase
new file mode 100755 (executable)
index 0000000..fcedb95
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
+
+import inspect
+import optparse
+import os
+import sys
+import unittest
+
+localmodule = os.path.realpath(os.path.join(
+        os.path.dirname(inspect.getfile(inspect.currentframe())),
+        '..'))
+print('localmodule: ' + localmodule)
+if localmodule not in sys.path:
+    sys.path.insert(0,localmodule)
+
+import fdroidserver.common
+import fdroidserver.install
+
+class InstallTest(unittest.TestCase):
+    '''fdroidserver/install.py'''
+
+    def test_devices(self):
+        config = dict()
+        config['sdk_path'] = os.getenv('ANDROID_HOME')
+        fdroidserver.common.config = config
+        config['adb'] = fdroidserver.common.find_sdk_tools_cmd('adb')
+        self.assertTrue(os.path.exists(config['adb']))
+        self.assertTrue(os.path.isfile(config['adb']))
+        devices = fdroidserver.install.devices()
+        self.assertIsInstance(devices, list, 'install.devices() did not return a list!')
+        for device in devices:
+            self.assertIsInstance(device, basestring)
+
+
+if __name__ == "__main__":
+    parser = optparse.OptionParser()
+    parser.add_option("-v", "--verbose", action="store_true", default=False,
+                      help="Spew out even more information than normal")
+    (fdroidserver.install.options, args) = parser.parse_args(['--verbose'])
+
+    newSuite = unittest.TestSuite()
+    newSuite.addTest(unittest.makeSuite(InstallTest))
+    unittest.main()
index ae566b74ef53646fd9b5b23f6d4665f652acb345..d1f988fa64a3f5ef6e0983c272fb6162be80838b 100755 (executable)
@@ -97,8 +97,9 @@ echo_header "test python getsig replacement"
 
 cd $WORKSPACE/tests/getsig
 ./make.sh
-cd $WORKSPACE/tests
-./update.TestCase
+for testcase in $WORKSPACE/tests/*.TestCase; do
+    $testcase
+done
 
 
 #------------------------------------------------------------------------------#
@@ -208,16 +209,20 @@ $fdroid init --keystore $KEYSTORE --android-home $FAKE_ANDROID_HOME
 #------------------------------------------------------------------------------#
 echo_header "check that 'fdroid init' fails when build-tools cannot be found"
 
-REPOROOT=`create_test_dir`
-FAKE_ANDROID_HOME=`create_test_dir`
-create_fake_android_home $FAKE_ANDROID_HOME
-rm -f $FAKE_ANDROID_HOME/build-tools/*/aapt
-KEYSTORE=$REPOROOT/keystore.jks
-cd $REPOROOT
-set +e
-$fdroid init --keystore $KEYSTORE --android-home $FAKE_ANDROID_HOME
-[ $? -eq 0 ] && exit 1
-set -e
+if [ -e /usr/bin/aapt ]; then
+    echo "/usr/bin/aapt exists, not running test"
+else
+    REPOROOT=`create_test_dir`
+    FAKE_ANDROID_HOME=`create_test_dir`
+    create_fake_android_home $FAKE_ANDROID_HOME
+    rm -f $FAKE_ANDROID_HOME/build-tools/*/aapt
+    KEYSTORE=$REPOROOT/keystore.jks
+    cd $REPOROOT
+    set +e
+    $fdroid init --keystore $KEYSTORE --android-home $FAKE_ANDROID_HOME
+    [ $? -eq 0 ] && exit 1
+    set -e
+fi
 
 
 #------------------------------------------------------------------------------#
diff --git a/tests/urzip-release-unsigned.apk b/tests/urzip-release-unsigned.apk
new file mode 100644 (file)
index 0000000..7bc2229
Binary files /dev/null and b/tests/urzip-release-unsigned.apk differ
diff --git a/tests/urzip-release.apk b/tests/urzip-release.apk
new file mode 100644 (file)
index 0000000..28a0345
Binary files /dev/null and b/tests/urzip-release.apk differ