diff --git a/MANIFEST.in b/MANIFEST.in index ca8eea6e..ecefc9ed 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,13 +5,15 @@ include fdroid include jenkins-build include makebuildserver include buildserver/config.buildserver.py -include buildserver/fixpaths.sh -include buildserver/cookbooks/android-ndk/recipes/default.rb -include buildserver/cookbooks/android-sdk/recipes/default.rb -include buildserver/cookbooks/fdroidbuild-general/recipes/default.rb -include buildserver/cookbooks/gradle/recipes/default.rb -include buildserver/cookbooks/gradle/recipes/gradle -include buildserver/cookbooks/kivy/recipes/default.rb +include buildserver/provision-android-ndk +include buildserver/provision-android-sdk +include buildserver/provision-apt-get-install +include buildserver/provision-apt-proxy +include buildserver/provision-gradle +include buildserver/provision-pip +include buildserver/provision-qt-sdk +include buildserver/provision-ubuntu-trusty-paramiko +include buildserver/Vagrantfile include completion/bash-completion include docs/fdl.texi include docs/fdroid.texi diff --git a/examples/config.py b/examples/config.py index 63edc718..3b1ab95c 100644 --- a/examples/config.py +++ b/examples/config.py @@ -86,7 +86,7 @@ The repository of older versions of applications from the main demo repository. # current_version_name_source = 'id' # Optionally, override home directory for gpg -# gpghome = /home/fdroid/somewhere/else/.gnupg +# gpghome = '/home/fdroid/somewhere/else/.gnupg' # The ID of a GPG key for making detached signatures for apks. Optional. # gpgkey = '1DBA2E89' diff --git a/fdroidserver/build.py b/fdroidserver/build.py index daf109dd..cbbed3cd 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -412,7 +412,7 @@ def build_server(app, build, vcs, build_dir, output_dir, force): ftp.chdir(homedir + '/tmp') else: ftp.chdir(homedir + '/unsigned') - apkfile = common.getapkname(app, build) + apkfile = common.get_release_filename(app, build) tarball = common.getsrcname(app, build) try: ftp.get(apkfile, os.path.join(output_dir, apkfile)) @@ -454,6 +454,54 @@ def capitalize_intact(string): return string[0].upper() + string[1:] +def get_metadata_from_apk(app, build, apkfile): + """get the required metadata from the built APK""" + + p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False) + + vercode = None + version = None + foundid = None + nativecode = None + for line in p.output.splitlines(): + if line.startswith("package:"): + pat = re.compile(".*name='([a-zA-Z0-9._]*)'.*") + m = pat.match(line) + if m: + foundid = m.group(1) + pat = re.compile(".*versionCode='([0-9]*)'.*") + m = pat.match(line) + if m: + vercode = m.group(1) + pat = re.compile(".*versionName='([^']*)'.*") + m = pat.match(line) + if m: + version = m.group(1) + elif line.startswith("native-code:"): + nativecode = line[12:] + + # Ignore empty strings or any kind of space/newline chars that we don't + # care about + if nativecode is not None: + nativecode = nativecode.strip() + nativecode = None if not nativecode else nativecode + + if build.buildjni and build.buildjni != ['no']: + if nativecode is None: + raise BuildException("Native code should have been built but none was packaged") + if build.novcheck: + vercode = build.vercode + version = build.version + if not version or not vercode: + raise BuildException("Could not find version information in build in output") + if not foundid: + raise BuildException("Could not find package ID in output") + if foundid != app.id: + raise BuildException("Wrong package ID - build " + foundid + " but expected " + app.id) + + return vercode, version + + def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir, tmp_dir, force, onserver, refresh): """Do a build locally.""" @@ -809,7 +857,7 @@ def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir, src = os.path.normpath(apks[0]) # Make sure it's not debuggable... - if common.isApkDebuggable(src, config): + if common.isApkAndDebuggable(src, config): raise BuildException("APK is debuggable") # By way of a sanity check, make sure the version and version @@ -818,64 +866,17 @@ def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir, if not os.path.exists(src): raise BuildException("Unsigned apk is not at expected location of " + src) - p = SdkToolsPopen(['aapt', 'dump', 'badging', src], output=False) - - vercode = None - version = None - foundid = None - nativecode = None - for line in p.output.splitlines(): - if line.startswith("package:"): - pat = re.compile(".*name='([a-zA-Z0-9._]*)'.*") - m = pat.match(line) - if m: - foundid = m.group(1) - pat = re.compile(".*versionCode='([0-9]*)'.*") - m = pat.match(line) - if m: - vercode = m.group(1) - pat = re.compile(".*versionName='([^']*)'.*") - m = pat.match(line) - if m: - version = m.group(1) - elif line.startswith("native-code:"): - nativecode = line[12:] - - # Ignore empty strings or any kind of space/newline chars that we don't - # care about - if nativecode is not None: - nativecode = nativecode.strip() - nativecode = None if not nativecode else nativecode - - if build.buildjni and build.buildjni != ['no']: - if nativecode is None: - raise BuildException("Native code should have been built but none was packaged") - if build.novcheck: + if common.get_file_extension(src) == 'apk': + vercode, version = get_metadata_from_apk(app, build, src) + if (version != build.version or vercode != build.vercode): + raise BuildException(("Unexpected version/version code in output;" + " APK: '%s' / '%s', " + " Expected: '%s' / '%s'") + % (version, str(vercode), build.version, + str(build.vercode))) + else: vercode = build.vercode version = build.version - if not version or not vercode: - raise BuildException("Could not find version information in build in output") - if not foundid: - raise BuildException("Could not find package ID in output") - if foundid != app.id: - raise BuildException("Wrong package ID - build " + foundid + " but expected " + app.id) - - # Some apps (e.g. Timeriffic) have had the bonkers idea of - # including the entire changelog in the version number. Remove - # it so we can compare. (TODO: might be better to remove it - # before we compile, in fact) - index = version.find(" //") - if index != -1: - version = version[:index] - - if (version != build.version or - vercode != build.vercode): - raise BuildException(("Unexpected version/version code in output;" - " APK: '%s' / '%s', " - " Expected: '%s' / '%s'") - % (version, str(vercode), build.version, - str(build.vercode)) - ) # Add information for 'fdroid verify' to be able to reproduce the build # environment. @@ -892,7 +893,7 @@ def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir, # Copy the unsigned apk to our destination directory for further # processing (by publish.py)... - dest = os.path.join(output_dir, common.getapkname(app, build)) + dest = os.path.join(output_dir, common.get_release_filename(app, build)) shutil.copyfile(src, dest) # Move the source tarball into the output directory... @@ -920,17 +921,17 @@ def trybuild(app, build, build_dir, output_dir, also_check_dir, srclib_dir, extl :returns: True if the build was done, False if it wasn't necessary. """ - dest_apk = common.getapkname(app, build) + dest_file = common.get_release_filename(app, build) - dest = os.path.join(output_dir, dest_apk) - dest_repo = os.path.join(repo_dir, dest_apk) + dest = os.path.join(output_dir, dest_file) + dest_repo = os.path.join(repo_dir, dest_file) if not test: if os.path.exists(dest) or os.path.exists(dest_repo): return False if also_check_dir: - dest_also = os.path.join(also_check_dir, dest_apk) + dest_also = os.path.join(also_check_dir, dest_file) if os.path.exists(dest_also): return False diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 1710c405..08708f31 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -459,8 +459,11 @@ def apknameinfo(filename): return result -def getapkname(app, build): - return "%s_%s.apk" % (app.id, build.vercode) +def get_release_filename(app, build): + if build.output: + return "%s_%s.%s" % (app.id, build.vercode, get_file_extension(build.output)) + else: + return "%s_%s.apk" % (app.id, build.vercode) def getsrcname(app, build): @@ -1615,11 +1618,14 @@ def get_file_extension(filename): return os.path.splitext(filename)[1].lower()[1:] -def isApkDebuggable(apkfile, config): - """Returns True if the given apk file is debuggable +def isApkAndDebuggable(apkfile, config): + """Returns True if the given file is an APK and is debuggable :param apkfile: full path to the apk to check""" + if get_file_extension(apkfile) != 'apk': + return False + p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'], output=False) if p.returncode != 0: @@ -2078,3 +2084,14 @@ def get_per_app_repos(): repos.append(d) break return repos + + +def is_repo_file(filename): + '''Whether the file in a repo is a build product to be delivered to users''' + return os.path.isfile(filename) \ + and os.path.basename(filename) not in [ + 'index.jar', + 'index.xml', + 'index.html', + 'categories.txt', + ] diff --git a/fdroidserver/gpgsign.py b/fdroidserver/gpgsign.py index 41b5a43f..4c9cf6bb 100644 --- a/fdroidserver/gpgsign.py +++ b/fdroidserver/gpgsign.py @@ -50,10 +50,13 @@ def main(): sys.exit(1) # Process any apks that are waiting to be signed... - for apkfile in sorted(glob.glob(os.path.join(output_dir, '*.apk'))): - - apkfilename = os.path.basename(apkfile) - sigfilename = apkfilename + ".asc" + for f in sorted(glob.glob(os.path.join(output_dir, '*.*'))): + if common.get_file_extension(f) == 'asc': + continue + if not common.is_repo_file(f): + continue + filename = os.path.basename(f) + sigfilename = filename + ".asc" sigpath = os.path.join(output_dir, sigfilename) if not os.path.exists(sigpath): @@ -64,13 +67,13 @@ def main(): gpgargs.extend(['--homedir', config['gpghome']]) if 'gpgkey' in config: gpgargs.extend(['--local-user', config['gpgkey']]) - gpgargs.append(os.path.join(output_dir, apkfilename)) + gpgargs.append(os.path.join(output_dir, filename)) p = FDroidPopen(gpgargs) if p.returncode != 0: logging.error("Signing failed.") sys.exit(1) - logging.info('Signed ' + apkfilename) + logging.info('Signed ' + filename) if __name__ == "__main__": diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 30390f82..110de3ef 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -517,13 +517,11 @@ def scan_repo_files(apkcache, repodir, knownapks, use_date_from_file=False): cachechanged = False repo_files = [] for name in os.listdir(repodir): - if name in ['index.jar', 'index.xml', 'index.html', 'categories.txt', ]: - continue file_extension = common.get_file_extension(name) if file_extension == 'apk' or file_extension == 'obb': continue filename = os.path.join(repodir, name) - if not os.path.isfile(filename): + if not common.is_repo_file(name): continue stat = os.stat(filename) if stat.st_size == 0: @@ -739,7 +737,7 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False): apk['minSdkVersion'] = 1 # Check for debuggable apks... - if common.isApkDebuggable(apkfile, config): + if common.isApkAndDebuggable(apkfile, config): logging.warning('{0} is set to android:debuggable="true"'.format(apkfile)) # Get the signature (or md5 of, to be precise)... diff --git a/tests/common.TestCase b/tests/common.TestCase index d7dca14e..d01568a2 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -74,7 +74,7 @@ class CommonTest(unittest.TestCase): 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) + debuggable = fdroidserver.common.isApkAndDebuggable(apkfile, config) self.assertTrue(debuggable, "debuggable APK state was not properly parsed!") # these are set NOT debuggable @@ -82,7 +82,7 @@ class CommonTest(unittest.TestCase): 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) + debuggable = fdroidserver.common.isApkAndDebuggable(apkfile, config) self.assertFalse(debuggable, "debuggable APK state was not properly parsed!") diff --git a/tests/gnupghome/pubring.gpg b/tests/gnupghome/pubring.gpg new file mode 100644 index 00000000..fc60c42a Binary files /dev/null and b/tests/gnupghome/pubring.gpg differ diff --git a/tests/gnupghome/random_seed b/tests/gnupghome/random_seed new file mode 100644 index 00000000..cb41f6e0 Binary files /dev/null and b/tests/gnupghome/random_seed differ diff --git a/tests/gnupghome/secring.gpg b/tests/gnupghome/secring.gpg new file mode 100644 index 00000000..20b16086 Binary files /dev/null and b/tests/gnupghome/secring.gpg differ diff --git a/tests/gnupghome/trustdb.gpg b/tests/gnupghome/trustdb.gpg new file mode 100644 index 00000000..7a1fe0f5 Binary files /dev/null and b/tests/gnupghome/trustdb.gpg differ diff --git a/tests/repo/fake.ota.update_1234.zip b/tests/repo/fake.ota.update_1234.zip new file mode 100644 index 00000000..7443d70d Binary files /dev/null and b/tests/repo/fake.ota.update_1234.zip differ diff --git a/tests/repo/obb.main.twoversions_1101617_src.tar.gz b/tests/repo/obb.main.twoversions_1101617_src.tar.gz new file mode 100644 index 00000000..3e086c60 Binary files /dev/null and b/tests/repo/obb.main.twoversions_1101617_src.tar.gz differ diff --git a/tests/run-tests b/tests/run-tests index c681a2ba..a4474daa 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -139,21 +139,33 @@ $fdroid update #------------------------------------------------------------------------------# -echo_header "copy tests/repo, generate a keystore, and update" +echo_header "copy tests/repo, generate java/gpg keys, update, and gpgsign" REPOROOT=`create_test_dir` +GNUPGHOME=$REPOROOT/gnupghome cd $REPOROOT $fdroid init cp -a $WORKSPACE/tests/metadata $WORKSPACE/tests/repo $REPOROOT/ +cp -a $WORKSPACE/tests/gnupghome $GNUPGHOME +chmod 0700 $GNUPGHOME echo "accepted_formats = ['json', 'txt', 'xml', 'yml']" >> config.py echo "install_list = 'org.adaway'" >> config.py echo "uninstall_list = {'com.android.vending', 'com.facebook.orca',}" >> config.py +echo "gpghome = '$GNUPGHOME'" >> config.py +echo "gpgkey = 'CE71F7FB'" >> config.py $fdroid update --verbose test -e repo/index.xml test -e repo/index.jar grep -F 'source tarball '; $out.=$this->human_readable_size(filesize($this->site_path.'/repo/'.$apk['srcname'])); + if(file_exists($this->site_path.'/repo/'.$apk['srcname'].'.asc')) { + $out.=' GPG Signature '; + } } if(isset($apk['permissions'])) {