From ad2b9b99c2a7084e1ef4df06d635c7b63bee89e3 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 7 Nov 2016 11:39:33 +0100 Subject: [PATCH 1/8] log versions of all installed Android SDK/NDK components Any variation in the Android tools used to build an APK can cause the build to be unreproducible. To help troubleshoot these times, this posts the installed versions of the Android SDK and NDK components to the lastbuild log, for the long term record. refs #148 --- fdroidserver/build.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/fdroidserver/build.py b/fdroidserver/build.py index ac9ae552..75539a28 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -952,6 +952,40 @@ def trybuild(app, build, build_dir, output_dir, also_check_dir, srclib_dir, extl return True +def get_android_tools_versions(sdk_path, ndk_path=None): + '''get a list of the versions of all installed Android SDK/NDK components''' + + if sdk_path[-1] != '/': + sdk_path += '/' + components = [] + if ndk_path: + ndk_release_txt = os.path.join(ndk_path, 'RELEASE.TXT') + if os.path.isfile(ndk_release_txt): + with open(ndk_release_txt, 'r') as fp: + components.append((os.path.basename(ndk_path), fp.read()[:-1])) + + pattern = re.compile('^Pkg.Revision=(.+)', re.MULTILINE) + for root, dirs, files in os.walk(sdk_path): + if 'source.properties' in files: + source_properties = os.path.join(root, 'source.properties') + with open(source_properties, 'r') as fp: + m = pattern.search(fp.read()) + if m: + components.append((root[len(sdk_path):], m.group(1))) + + return components + + +def get_android_tools_version_log(sdk_path, ndk_path): + '''get a list of the versions of all installed Android SDK/NDK components''' + log = '' + components = get_android_tools_versions(sdk_path, ndk_path) + for name, version in sorted(components): + log += '* ' + name + ' (' + version + ')\n' + + return log + + def parse_commandline(): """Parse the command line. Returns options, parser.""" @@ -1099,6 +1133,8 @@ def main(): for build in app.builds: wikilog = None + tools_version_log = '== Installed Android Tools ==\n\n' + tools_version_log += get_android_tools_version_log(config['sdk_path'], build.ndk_path()) try: # For the first build of a particular app, we need to set up @@ -1164,6 +1200,9 @@ def main(): failed_apps[appid] = e wikilog = str(e) + if wikilog: + wikilog = tools_version_log + '\n\n' + wikilog + if options.wiki and wikilog: try: # Write a page with the last build log for this version code From 3fb4cba178167c97ef11c100d5422a3ec857adb7 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 7 Nov 2016 15:36:16 +0100 Subject: [PATCH 2/8] include version, commit, and android tools versions in local log This includes more info to help track down problems with reproducible builds, like the specific version being built, and which exact versions of the Android SDK and NDK were used. --- fdroidserver/build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fdroidserver/build.py b/fdroidserver/build.py index 75539a28..5aa2d7c6 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -1186,6 +1186,12 @@ def main(): wikilog = str(vcse) except FDroidException as e: with open(os.path.join(log_dir, appid + '.log'), 'a+') as f: + f.write('\n\n============================================================\n') + f.write('versionCode: %s\nversionName: %s\ncommit: %s\n' % + (build.vercode, build.version, build.commit)) + f.write('Build completed at ' + + time.strftime("%Y-%m-%d %H:%M:%SZ", time.gmtime()) + '\n') + f.write('\n' + tools_version_log + '\n') f.write(str(e)) logging.error("Could not build app %s: %s" % (appid, e)) if options.stop: From 1f55a40caab2114fef088c683a86f47072be9e6f Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 7 Nov 2016 21:21:32 +0100 Subject: [PATCH 3/8] properly parse build metadata list types like gradle= Something like `gradle: yes` in YAML will be parsed as a boolean, since 'yes' is officially defined as a boolean true in YAML. For metadata fields that need to be lists, this needs to be converted. Same goes for a single string like `gradle: customFlavor`. --- fdroidserver/metadata.py | 5 +++++ tests/metadata/net.osmand.plus.pickle | Bin 4914 -> 4090 bytes tests/metadata/org.adaway.pickle | Bin 15877 -> 11751 bytes tests/metadata/org.videolan.vlc.pickle | Bin 38517 -> 29647 bytes 4 files changed, 5 insertions(+) diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index ddfb19b5..0df2ece0 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -904,6 +904,11 @@ def post_metadata_parse(app): elif ftype == TYPE_STRING: if isinstance(v, bool) and v: build.__dict__[k] = 'yes' + elif ftype == TYPE_LIST: + if isinstance(v, bool) and v: + build.__dict__[k] = ['yes'] + elif isinstance(v, str): + build.__dict__[k] = [v] if not app.Description: app.Description = 'No description available' diff --git a/tests/metadata/net.osmand.plus.pickle b/tests/metadata/net.osmand.plus.pickle index cf9a39ec1f83b3f2393b562f560b1872d1e5ec83..76409710f731bf7434872473ad0d52e27768d5f3 100644 GIT binary patch literal 4090 zcmb_fX?Gj96;vU zrd9;c@>FQLqe*v;d-q{_C*}UWiaK<{t6=EH(Ml?WD>AxE*ad#v?TRSVboW)d$F5o8 zndro+f}MMtblqRfC{k^9J>=bu*-2;KnWl%mllZ-x1UQr}N#XCa2G2M>;(GU+t&&F_5yl;Q)WdLu zPTJ`$k;H7Lp9p$`cLLKLmC<9~1Naqko#obAK#vzT>UkD3mv;rdQJ(Z3#-e)aOVtx> zGY*yCr#HEF>vo){KAeaHL2ve+!aA8{i9T1W!Mx1d4nkFnb(cp0PmMyno&~GEy9KZA`PhVRo6%Bi0s7 zT**Wfh=8^GOhmjLBF!w5f+b$&C~F=0(bhgE6f5r{pWhZ)N?4413BB9n2T9R`;w)};jGfCa#ZQHvh#qzWFO z`b&p`_#g^nZX#`?bbxA5g?Fd}8vsN-6-;TyakAde;)Eq>+(~(t@g7$pl4HVc5gmxO zRw#C^*?|Z{d$gY-w1iymw5H~~UYRTwxtRyn4vek?oDD2cozukdDdkUp%LJF5q z)Rvx1C&JeZ)z2fC<6%Ej7;BOC5cE8OLnXGdbV8*wlPFsgaK^@phAs>s&<{{(-=aGicPP_b*IW+?eeyts?`#-v>Q>j@&ai$0A|i`mx=H1%8(E zkfkCN9Mv&lXwXNxMAb}T1~7NPqIRyr09?^on)`;3nn@1PXbTasZrnpTn8>n%uvlob zo5ryXpy6Q(m71uGSwmwE+mR@c1KwxwX-MIrQZXLJNWLkog0OkJ8wWYOaVm__Q&ul| zfC{9i3rPjqpMhT1s7>-V556UwAnVQ zk;bizne0OEhM7Q8=ou^M+j+Ok(>^`xjl$AK!M@s2{V=6>gMxIrP(LHO6V?J`tR4l| zsA@-RsMbgeaxAj*n`^x(s#y=vBYeoGk({*XF=t%s=}O46EM0BTWC3SY>s%D95GkGY zo`qv&asCgzt|_xm)0B4)HrUi?dag;+<5y{>Xmu(=HN<*#d_ zwa!>U4$IODmfO%z-_XqV_q76uHF!BvwB97wLn|AJV%p#~a9uDdqRoLt1Dy;)K_6_= zmW@zpXWK5FbmpA1v}1jTn_O$rg&@#wlP=os83p@vsknIP)VB%nn)G66%9Im*sOYnE zXXzzts5lZDooM=SlbY5Kowoy((nsta0r5#=6__@U!ljn=rHIkX-oNh{8F6YzT@{B{ z?X06EJ9P?5o3UyG8FgT4Vf5*`+t}JzZM3!<7e?^`%a46?1s$Q9#(5@M;Ii>| zX>Gagww5+G5B=GMpY`+0t(^_)=+441=^nax=$pwN=8mm0NQ$rxlf7Wme0&yHu0U9P zumCye3H0)zD?mKlTQCNkz@&=0JjVnY_WY zDas|7)svT82^Jm5k~vd!MS>}Pc}se{B44(=a-16e3YvUWwg=K5$Y3DFbqVAjleY)= zD&Sr_3ink5_ca6e;|A_0Zi4&CL%8$KbMz?#_tQtwOScC81MsgO1^x{K{F?^&w+!%a-vs%4eKZl-Zh;Ra2=DV}6_x=Gy3?c?pY#r@B|=7!fC`_TH`jfd9nEwvgkq(aai zyyro!@=9Qs>2%__6AJr|ONN)6+EbhM9@x@(lEi7&!o{tHzHk|j6#AnLp$h0v_8ogn z!|2cUWb+oKzx=oRN4FR7Lvo|R4DWUH*W%G$;9KtEjX-}ZE;?(tj@j4q-`&?gEo~(J I;pWbN0L~jE!~g&Q literal 4914 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 diff --git a/tests/metadata/org.videolan.vlc.pickle b/tests/metadata/org.videolan.vlc.pickle index a1810e600d7f2c48e0c21a5ba2a8bda56294d535..8a727bb53a5501b6a8636a770a95e0bf0bd5a0db 100644 GIT binary patch literal 29647 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 From f8dca60a20f68e4e895dab5ddff9378e58e7ff3c Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 7 Nov 2016 21:27:21 +0100 Subject: [PATCH 4/8] convert comments above functions to python docstrings This is how to write per-function comments. https://www.python.org/dev/peps/pep-0257/ --- fdroidserver/common.py | 24 +++++++++++++++--------- fdroidserver/metadata.py | 32 ++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 80eb8a0e..a023c57d 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -358,9 +358,11 @@ def get_local_metadata_files(): return glob.glob('.fdroid.[a-jl-z]*[a-rt-z]') -# Given the arguments in the form of multiple appid:[vc] strings, this returns -# a dictionary with the set of vercodes specified for each package. def read_pkg_args(args, allow_vercodes=False): + """ + Given the arguments in the form of multiple appid:[vc] strings, this returns + a dictionary with the set of vercodes specified for each package. + """ vercodes = {} if not args: @@ -380,9 +382,11 @@ def read_pkg_args(args, allow_vercodes=False): return vercodes -# On top of what read_pkg_args does, this returns the whole app metadata, but -# limiting the builds list to the builds matching the vercodes specified. def read_app_args(args, allapps, allow_vercodes=False): + """ + On top of what read_pkg_args does, this returns the whole app metadata, but + limiting the builds list to the builds matching the vercodes specified. + """ vercodes = read_pkg_args(args, allow_vercodes) @@ -979,8 +983,8 @@ def retrieve_string_singleline(app_dir, string, xmlfiles=None): return retrieve_string(app_dir, string, xmlfiles).replace('\n', ' ').strip() -# Return list of existing files that will be used to find the highest vercode def manifest_paths(app_dir, flavours): + '''Return list of existing files that will be used to find the highest vercode''' possible_manifests = \ [os.path.join(app_dir, 'AndroidManifest.xml'), @@ -997,8 +1001,8 @@ def manifest_paths(app_dir, flavours): return [path for path in possible_manifests if os.path.isfile(path)] -# Retrieve the package name. Returns the name, or None if not found. def fetch_real_name(app_dir, flavours): + '''Retrieve the package name. Returns the name, or None if not found.''' for path in manifest_paths(app_dir, flavours): if not has_extension(path, 'xml') or not os.path.isfile(path): continue @@ -1070,10 +1074,12 @@ def app_matches_packagename(app, package): return appid == package -# Extract some information from the AndroidManifest.xml at the given path. -# Returns (version, vercode, package), any or all of which might be None. -# All values returned are strings. def parse_androidmanifests(paths, app): + """ + Extract some information from the AndroidManifest.xml at the given path. + Returns (version, vercode, package), any or all of which might be None. + All values returned are strings. + """ ignoreversions = app.UpdateCheckIgnore ignoresearch = re.compile(ignoreversions).search if ignoreversions else None diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index 0df2ece0..f8dc2b5c 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -152,24 +152,30 @@ class App(): self.lastupdated = None self._modified = set() - # Translates human-readable field names to attribute names, e.g. - # 'Auto Name' to 'AutoName' @classmethod def field_to_attr(cls, f): + """ + Translates human-readable field names to attribute names, e.g. + 'Auto Name' to 'AutoName' + """ return f.replace(' ', '') - # Translates attribute names to human-readable field names, e.g. - # 'AutoName' to 'Auto Name' @classmethod def attr_to_field(cls, k): + """ + Translates attribute names to human-readable field names, e.g. + 'AutoName' to 'Auto Name' + """ if k in app_fields: return k f = re.sub(r'([a-z])([A-Z])', r'\1 \2', k) return f - # Constructs an old-fashioned dict with the human-readable field - # names. Should only be used for tests. def field_dict(self): + """ + Constructs an old-fashioned dict with the human-readable field + names. Should only be used for tests. + """ d = {} for k, v in self.__dict__.items(): if k == 'builds': @@ -182,23 +188,23 @@ class App(): d[f] = v return d - # Gets the value associated to a field name, e.g. 'Auto Name' def get_field(self, f): + """Gets the value associated to a field name, e.g. 'Auto Name'""" if f not in app_fields: warn_or_exception('Unrecognised app field: ' + f) k = App.field_to_attr(f) return getattr(self, k) - # Sets the value associated to a field name, e.g. 'Auto Name' def set_field(self, f, v): + """Sets the value associated to a field name, e.g. 'Auto Name'""" if f not in app_fields: warn_or_exception('Unrecognised app field: ' + f) k = App.field_to_attr(f) self.__dict__[k] = v self._modified.add(k) - # Appends to the value associated to a field name, e.g. 'Auto Name' def append_field(self, f, v): + """Appends to the value associated to a field name, e.g. 'Auto Name'""" if f not in app_fields: warn_or_exception('Unrecognised app field: ' + f) k = App.field_to_attr(f) @@ -207,8 +213,8 @@ class App(): else: self.__dict__[k].append(v) - # Like dict.update(), but using human-readable field names def update_fields(self, d): + '''Like dict.update(), but using human-readable field names''' for f, v in d.items(): if f == 'builds': for b in v: @@ -772,9 +778,11 @@ def read_srclibs(): srclibs[srclibname] = parse_srclib(metadatapath) -# Read all metadata. Returns a list of 'app' objects (which are dictionaries as -# returned by the parse_txt_metadata function. def read_metadata(xref=True): + """ + Read all metadata. Returns a list of 'app' objects (which are dictionaries as + returned by the parse_txt_metadata function. + """ # Always read the srclibs before the apps, since they can use a srlib as # their source repository. From b4a39ee27250e8c98023d673e9bc4d75f3b4fb5f Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 7 Nov 2016 23:24:52 +0100 Subject: [PATCH 5/8] switch import test to custom, small test app The test project should be moved to https://gitlab.com/fdroid/ci-test-app --- tests/import.TestCase | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/import.TestCase b/tests/import.TestCase index 7b846a5f..8eef5acb 100755 --- a/tests/import.TestCase +++ b/tests/import.TestCase @@ -30,7 +30,7 @@ class ImportTest(unittest.TestCase): fdroidserver.common.fill_config_defaults(config) fdroidserver.common.config = config - url = 'https://gitlab.com/fdroid/fdroidclient' + url = 'https://gitlab.com/eighthave/ci-test-app' r = requests.head(url) if r.status_code != 200: print("ERROR", url, 'unreachable (', r.status_code, ')') @@ -41,8 +41,8 @@ class ImportTest(unittest.TestCase): app.UpdateCheckMode = "Tags" root_dir, src_dir = import_proxy.get_metadata_from_url(app, url) self.assertEqual(app.RepoType, 'git') - self.assertEqual(app.WebSite, 'https://gitlab.com/fdroid/fdroidclient') - self.assertEqual(app.Repo, 'https://gitlab.com/fdroid/fdroidclient.git') + self.assertEqual(app.WebSite, 'https://gitlab.com/eighthave/ci-test-app') + self.assertEqual(app.Repo, 'https://gitlab.com/eighthave/ci-test-app.git') if __name__ == "__main__": From a4e4310803a463433eb7515c2038a8d3ea44edc4 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 7 Nov 2016 21:47:53 +0100 Subject: [PATCH 6/8] allow metadata to be embedded in source repos via .fdroid.yml This allows a source repo to include a complete metadata file so that it can be built directly in place using `fdroid build`. If that app is then included in fdroiddata, it will first load the source repo type and URL from fdroiddata, then read .fdroid.yml if it exists, then include the rest of the metadata as specified in fdroiddata, so that fdroiddata has precedence over the metadata in the source code. This lets `fdroid build` apps without having a whole fdroiddata setup, but instead just directly in place in the source code. This also lets devs optionallu maintain the fdroid metadata as part of their app, rather than in fdroiddata without loosing any control. This should make it easier to spread around the maintenance load. --- fdroidserver/build.py | 16 ++---- fdroidserver/common.py | 21 ++++++++ fdroidserver/metadata.py | 52 +++++++++++++++++-- tests/import.TestCase | 1 + .../metadata/info.guardianproject.checkey.txt | 21 ++++++++ tests/metadata/org.fdroid.ci.test.app.txt | 2 + tests/metadata/raw.template.txt | 9 ++++ tests/run-tests | 15 ++++++ 8 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 tests/metadata/info.guardianproject.checkey.txt create mode 100644 tests/metadata/org.fdroid.ci.test.app.txt create mode 100644 tests/metadata/raw.template.txt diff --git a/fdroidserver/build.py b/fdroidserver/build.py index 5aa2d7c6..281ad523 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -1100,9 +1100,10 @@ def main(): extlib_dir = os.path.join(build_dir, 'extlib') # Read all app and srclib metadata - allapps = metadata.read_metadata(xref=not options.onserver) - + pkgs = common.read_pkg_args(options.appid, True) + allapps = metadata.read_metadata(not options.onserver, pkgs) apps = common.read_app_args(options.appid, allapps, True) + for appid, app in list(apps.items()): if (app.Disabled and not options.force) or not app.RepoType or not app.builds: del apps[appid] @@ -1141,16 +1142,7 @@ def main(): # the source repo. We can reuse it on subsequent builds, if # there are any. if first: - if app.RepoType == 'srclib': - build_dir = os.path.join('build', 'srclib', app.Repo) - else: - build_dir = os.path.join('build', appid) - - # Set up vcs interface and make sure we have the latest code... - logging.debug("Getting {0} vcs interface for {1}" - .format(app.RepoType, app.Repo)) - vcs = common.getvcs(app.RepoType, app.Repo, build_dir) - + vcs, build_dir = common.setup_vcs(app) first = False logging.debug("Checking " + build.version) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index a023c57d..121f6edd 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -486,6 +486,27 @@ def getcvname(app): return '%s (%s)' % (app.CurrentVersion, app.CurrentVersionCode) +def get_build_dir(app): + '''get the dir that this app will be built in''' + + if app.RepoType == 'srclib': + return os.path.join('build', 'srclib', app.Repo) + + return os.path.join('build', app.id) + + +def setup_vcs(app): + '''checkout code from VCS and return instance of vcs and the build dir''' + build_dir = get_build_dir(app) + + # Set up vcs interface and make sure we have the latest code... + logging.debug("Getting {0} vcs interface for {1}" + .format(app.RepoType, app.Repo)) + vcs = getvcs(app.RepoType, app.Repo, build_dir) + + return vcs, build_dir + + def getvcs(vcstype, remote, local): if vcstype == 'git': return vcs_git(remote, local) diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index f8dc2b5c..986d240a 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -224,6 +224,21 @@ class App(): else: self.set_field(f, v) + def update(self, d): + '''Like dict.update()''' + for k, v in d.__dict__.items(): + if k == '_modified': + continue + elif k == 'builds': + for b in v: + build = Build() + del(b.__dict__['_modified']) + build.update_flags(b.__dict__) + self.builds.append(build) + elif v: + self.__dict__[k] = v + self._modified.add(k) + TYPE_UNKNOWN = 0 TYPE_OBSOLETE = 1 @@ -778,10 +793,12 @@ def read_srclibs(): srclibs[srclibname] = parse_srclib(metadatapath) -def read_metadata(xref=True): +def read_metadata(xref=True, check_vcs=[]): """ Read all metadata. Returns a list of 'app' objects (which are dictionaries as returned by the parse_txt_metadata function. + + check_vcs is the list of packageNames to check for .fdroid.yml in source """ # Always read the srclibs before the apps, since they can use a srlib as @@ -809,7 +826,7 @@ def read_metadata(xref=True): packageName, _ = fdroidserver.common.get_extension(os.path.basename(metadatapath)) if packageName in apps: warn_or_exception("Found multiple metadata files for " + packageName) - app = parse_metadata(metadatapath) + app = parse_metadata(metadatapath, packageName in check_vcs) check_metadata(app) apps[app.id] = app @@ -962,7 +979,9 @@ def _decode_bool(s): warn_or_exception("Invalid bool '%s'" % s) -def parse_metadata(metadatapath): +def parse_metadata(metadatapath, check_vcs=False): + '''parse metadata file, optionally checking the git repo for metadata first''' + _, ext = fdroidserver.common.get_extension(metadatapath) accepted = fdroidserver.common.config['accepted_formats'] if ext not in accepted: @@ -971,7 +990,11 @@ def parse_metadata(metadatapath): app = App() app.metadatapath = metadatapath - app.id, _ = fdroidserver.common.get_extension(os.path.basename(metadatapath)) + name, _ = fdroidserver.common.get_extension(os.path.basename(metadatapath)) + if name == '.fdroid': + check_vcs = False + else: + app.id = name with open(metadatapath, 'r', encoding='utf-8') as mf: if ext == 'txt': @@ -985,7 +1008,28 @@ def parse_metadata(metadatapath): else: warn_or_exception('Unknown metadata format: %s' % metadatapath) + if check_vcs and app.Repo: + build_dir = fdroidserver.common.get_build_dir(app) + metadata_in_repo = os.path.join(build_dir, '.fdroid.yml') + if not os.path.isfile(metadata_in_repo): + vcs, build_dir = fdroidserver.common.setup_vcs(app) + vcs.gotorevision('HEAD') # HEAD since we can't know where else to go + if os.path.isfile(metadata_in_repo): + logging.debug('Including metadata from ' + metadata_in_repo) + app.update(parse_metadata(metadata_in_repo)) + post_metadata_parse(app) + + if not app.id: + if app.builds: + build = app.builds[-1] + if build.subdir: + root_dir = build.subdir + else: + root_dir = '.' + paths = fdroidserver.common.manifest_paths(root_dir, build.gradle) + _, _, app.id = fdroidserver.common.parse_androidmanifests(paths, app) + return app diff --git a/tests/import.TestCase b/tests/import.TestCase index 8eef5acb..3cfdb92e 100755 --- a/tests/import.TestCase +++ b/tests/import.TestCase @@ -25,6 +25,7 @@ class ImportTest(unittest.TestCase): '''fdroid import''' def test_import_gitlab(self): + os.chdir(os.path.dirname(__file__)) # FDroidPopen needs some config to work config = dict() fdroidserver.common.fill_config_defaults(config) diff --git a/tests/metadata/info.guardianproject.checkey.txt b/tests/metadata/info.guardianproject.checkey.txt new file mode 100644 index 00000000..43faaade --- /dev/null +++ b/tests/metadata/info.guardianproject.checkey.txt @@ -0,0 +1,21 @@ +Categories:Development,GuardianProject +License:GPLv3 +Web Site:https://dev.guardianproject.info/projects/checkey +Source Code:https://github.com/guardianproject/checkey +Issue Tracker:https://dev.guardianproject.info/projects/checkey/issues +Bitcoin:1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk + +Auto Name:Checkey +Summary:Info on local apps +Description: +Checkey is a utility for getting information about the APKs that are installed +on your device. Starting with a list of all of the apps that you have +installed on your device, it will show you the APK signature with a single +touch, and provides links to virustotal.com and androidobservatory.org to +easily access the profiles of that APK. It will also let you export the +signing certificate and generate ApkSignaturePin pin files for use with the +TrustedIntents library. +. + + +Current Version Code:9999999 diff --git a/tests/metadata/org.fdroid.ci.test.app.txt b/tests/metadata/org.fdroid.ci.test.app.txt new file mode 100644 index 00000000..466b7af6 --- /dev/null +++ b/tests/metadata/org.fdroid.ci.test.app.txt @@ -0,0 +1,2 @@ +Repo Type:git +Repo:https://gitlab.com/eighthave/ci-test-app diff --git a/tests/metadata/raw.template.txt b/tests/metadata/raw.template.txt new file mode 100644 index 00000000..ec2fe286 --- /dev/null +++ b/tests/metadata/raw.template.txt @@ -0,0 +1,9 @@ +License:Unknown +Web Site: +Source Code: +Issue Tracker: +Changelog: +Summary:Template +Description: +Template +. diff --git a/tests/run-tests b/tests/run-tests index a4474daa..354e6782 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -100,6 +100,8 @@ echo_header "test python getsig replacement" cd $WORKSPACE/tests/getsig ./make.sh + +cd $WORKSPACE/tests for testcase in $WORKSPACE/tests/*.TestCase; do $testcase done @@ -138,6 +140,19 @@ $fdroid readmeta $fdroid update +#------------------------------------------------------------------------------# +echo_header 'run `fdroid build` in fresh git checkout from import.TestCase' + +cd $WORKSPACE/tests/tmp/importer +if [ -d $ANDROID_HOME/platforms/android-23 ]; then + echo "build_tools = '`ls -1 $ANDROID_HOME/build-tools/ | sort -n | tail -1`'" > config.py + echo "force_build_tools = True" >> config.py + $fdroid build --verbose org.fdroid.ci.test.app:300 +else + echo 'WARNING: Skipping `fdroid build` test since android-23 is missing!' +fi + + #------------------------------------------------------------------------------# echo_header "copy tests/repo, generate java/gpg keys, update, and gpgsign" From c41daf1e7ee6acd724efed636b6744576d336582 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 8 Nov 2016 09:35:45 +0100 Subject: [PATCH 7/8] ignore files created by tests --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8bff77fa..40e2cd2b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ docs/html/ # files generated by tests tmp/ -tests/repo/icons* +/tests/repo/icons* # files used in manual testing /config.py @@ -24,6 +24,12 @@ tests/repo/icons* /logs/ /metadata/ makebuildserver.config.py +/tests/.fdroid.keypass.txt +/tests/.fdroid.keystorepass.txt /tests/config.py /tests/fdroid-icon.png +/tests/keystore.jks +/tests/OBBMainPatchCurrent.apk +/tests/OBBMainTwoVersions.apk +/tests/urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234.apk /unsigned/ From 28ea33b8d1a0dc14cebb20923353e7c9bd015d30 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 8 Nov 2016 16:26:22 +0100 Subject: [PATCH 8/8] if building directly in git repo, use file path for remote When a git repo has a .fdroid.yml file in it, and `fdroid build` is run directly in that git repo, then this uses the file path as the remote for the git repo in build/appid that is actually built. That makes it possible to run builds of commits that are only local, and makes things a whole lot faster. --- fdroidserver/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 121f6edd..e61d29be 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -502,7 +502,11 @@ def setup_vcs(app): # Set up vcs interface and make sure we have the latest code... logging.debug("Getting {0} vcs interface for {1}" .format(app.RepoType, app.Repo)) - vcs = getvcs(app.RepoType, app.Repo, build_dir) + if app.RepoType == 'git' and os.path.exists('.fdroid.yml'): + remote = os.getcwd() + else: + remote = app.Repo + vcs = getvcs(app.RepoType, remote, build_dir) return vcs, build_dir