From 2f2a09fe14561e0bc8ab9bcf85a56f7ca2c15a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20P=C3=B6hn?= Date: Tue, 4 Jun 2024 14:55:37 +0200 Subject: [PATCH] wip --- fdroidserver/build_local_prepare.py | 78 +----- fdroidserver/build_local_run.py | 355 ++++++++-------------------- fdroidserver/build_local_sudo.py | 104 ++++++++ tests/build_local_run.TestCase | 69 ++++++ 4 files changed, 281 insertions(+), 325 deletions(-) create mode 100644 fdroidserver/build_local_sudo.py create mode 100755 tests/build_local_run.TestCase diff --git a/fdroidserver/build_local_prepare.py b/fdroidserver/build_local_prepare.py index a495d0a8..7ac01631 100644 --- a/fdroidserver/build_local_prepare.py +++ b/fdroidserver/build_local_prepare.py @@ -19,58 +19,13 @@ import os import pathlib -import logging +# import logging import argparse from fdroidserver import _ import fdroidserver.common -def sudo_run(app, build): - # before doing anything, run the sudo commands to setup the VM - if build.sudo: - logging.info("Running 'sudo' commands in %s" % os.getcwd()) - - p = fdroidserver.common.FDroidPopen( - [ - 'sudo', - 'DEBIAN_FRONTEND=noninteractive', - 'bash', - '-e', - '-u', - '-o', - 'pipefail', - '-x', - '-c', - '; '.join(build.sudo), - ] - ) - if p.returncode != 0: - raise BuildException( - "Error running sudo command for %s:%s" % (app.id, build.versionName), - p.output, - ) - - -def sudo_lock_root(app, build): - p = fdroidserver.common.FDroidPopen(['sudo', 'passwd', '--lock', 'root']) - if p.returncode != 0: - raise BuildException( - "Error locking root account for %s:%s" % (app.id, build.versionName), - p.output, - ) - - -def sudo_uninstall(app, build): - p = fdroidserver.common.FDroidPopen( - ['sudo', 'SUDO_FORCE_REMOVE=yes', 'dpkg', '--purge', 'sudo'] - ) - if p.returncode != 0: - raise BuildException( - "Error removing sudo for %s:%s" % (app.id, build.versionName), p.output - ) - - def log_tools_version(app, build, log_dir): log_path = os.path.join( log_dir, fdroidserver.common.get_toolsversion_logname(app, build) @@ -86,24 +41,6 @@ def main(): "for one specific build" ), ) - parser.add_argument( - "--sudo-run", - action="store_true", - default=False, - help=_("run commands listed in sudo-metadata"), - ) - parser.add_argument( - "--sudo-uninstall", - action="store_true", - default=False, - help=_("uninstall sudo executing sudo-metadata"), - ) - parser.add_argument( - "--sudo-lock-root", - action="store_true", - default=False, - help=_("lock root user account"), - ) parser.add_argument( "APP_VERSION", help=_("app id and version code tuple 'APPID:VERCODE'"), @@ -114,6 +51,8 @@ def main(): options = fdroidserver.common.parse_args(parser) config = fdroidserver.common.get_config() + config # silence pyflakes + package_name, version_code = fdroidserver.common.split_pkg_arg(options.APP_VERSION) app, build = fdroidserver.metadata.read_build_metadata(package_name, version_code) @@ -126,17 +65,8 @@ def main(): for d in (srclib_dir, extlib_dir, log_dir, output_dir): d.mkdir(exist_ok=True, parents=True) - # run sudo stuff - fdroidserver.common.set_FDroidPopen_env(build) - if options.sudo_run: - sudo_run(app, build) - if options.sudo_lock_root: - sudo_lock_root(app, build) - if options.sudo_uninstall: - sudo_uninstall(app, build) - # TODO: in the past this was only logged when running as 'fdroid build - # --onserver' is this this output still valuable or can we remove it? + # --onserver', is this output still valuable or can we remove it? log_tools_version(app, build, log_dir) # do git/vcs checkout diff --git a/fdroidserver/build_local_run.py b/fdroidserver/build_local_run.py index 10f7caf5..13558416 100644 --- a/fdroidserver/build_local_run.py +++ b/fdroidserver/build_local_run.py @@ -1,8 +1,8 @@ import os +import re import sys import glob import shutil -import gettext import logging import pathlib import tarfile @@ -16,8 +16,17 @@ import fdroidserver.exception from fdroidserver import _ -def rlimit_check(apps_count): - """make sure enough open files are allowed to process everything""" +def rlimit_check(apps_count=1): + """Make sure linux is confgured to allow for enough simultaneously oepn files. + + TODO: check if this is obsolete + + Parameters + ---------- + apps_count + In the past this used to be `len(apps)` In this context we're + always buidling just one app so this is always 1 + """ try: import resource # not available on Windows @@ -37,7 +46,18 @@ def rlimit_check(apps_count): pass -def get_ndk_path(build): +def install_ndk(build, config): + """Make sure the requested NDK version is or gets installed + + TODO: check if this should be moved to a script that runs before starting + the build. e.g. `build_local_prepare` or `build_local_sudo` + + Parameters + ---------- + build + Metadata build entry that's about the build and may contain the + requested NDK version + """ ndk_path = build.ndk_path() if build.ndk or (build.buildjni and build.buildjni != ['no']): if not ndk_path: @@ -47,13 +67,11 @@ def get_ndk_path(build): if k.endswith("_orig"): continue logging.warning(" %s: %s" % (k, v)) - if onserver: - fdroidserver.common.auto_install_ndk(build) - else: - raise fdroidserver.exception.FDroidException() + fdroidserver.common.auto_install_ndk(build) elif not os.path.isdir(ndk_path): logging.critical("Android NDK '%s' is not a directory!" % ndk_path) raise fdroidserver.exception.FDroidException() + return ndk_path def get_build_root_dir(app, build): @@ -71,7 +89,7 @@ def transform_first_char(string, method): return method(string[0]) + string[1:] -def init_build_subprocess(app, build, config): +def init_build(app, build, config): # We need to clean via the build tool in case the binary dirs are # different from the default ones root_dir = get_build_root_dir(app, build) @@ -133,10 +151,21 @@ def init_build_subprocess(app, build, config): "Error cleaning %s:%s" % (app.id, build.versionName), p.output ) - return p, gradletasks, flavours_cmd + return gradletasks, flavours_cmd def sanitize_build_dir(app): + """delete build output directories + + This function deletes the default build/binary/target/... output + directories for follwoing build tools: gradle, maven, ant, jni. It also + deletes gradle-wrapper if present. + + Parameters + ---------- + app + The metadata of the app to sanitize + """ build_dir = fdroidserver.common.get_build_dir(app) for root, dirs, files in os.walk(build_dir): @@ -206,9 +235,8 @@ def make_tarball(app, build, tmp_dir): tarball.close() -def execute_build_commands(app, build): - build_dir = fdroidserver.common.get_build_dir(app) - srclibpaths = get_srclibpaths(build, build_dir) +def execute_build_commands(app, build, srclibpaths): + root_dir = get_build_root_dir(app, build) if build.build: logging.info("Running 'build' commands in %s" % root_dir) cmd = fdroidserver.common.replace_config_vars("; ".join(build.build), build) @@ -228,22 +256,23 @@ def execute_build_commands(app, build): ) -def get_srclibpaths(build, srclib_dir): +def checkout_srclibs(app, build): + """""" + build_dir = fdroidserver.common.get_build_dir(app) srclibpaths = [] if build.srclibs: logging.info("Collecting source libraries") for lib in build.srclibs: srclibpaths.append( fdroidserver.common.getsrclib( - lib, srclib_dir, preponly=onserver, refresh=refresh, build=build + lib, build_dir, prepare=False, refresh=False, build=build ) ) return srclibpaths -def execute_buildjni_commands(app, build): +def execute_buildjni_commands(app, build, ndk_path): root_dir = get_build_root_dir(app, build) - ndk_path = get_ndk_path(build) if build.buildjni and build.buildjni != ['no']: logging.info("Building the native code") jni_components = build.buildjni @@ -274,11 +303,12 @@ def execute_buildjni_commands(app, build): ) -def execute_build___(app, build, config, gradletasks): +def execute_build(app, build, config, gradletasks): build_dir = fdroidserver.common.get_build_dir(app) root_dir = get_build_root_dir(app, build) p = None + bindir = None bmethod = build.build_method() if bmethod == 'maven': logging.info("Building Maven project...") @@ -344,221 +374,10 @@ def execute_build___(app, build, config, gradletasks): bindir = os.path.join(root_dir, 'bin') - if os.path.isdir(os.path.join(build_dir, '.git')): - import git - - commit_id = fdroidserver.common.get_head_commit_id(git.repo.Repo(build_dir)) - else: - commit_id = build.commit - - if p is not None and p.returncode != 0: - raise fdroidserver.exception.BuildException( - "Build failed for %s:%s@%s" % (app.id, build.versionName, commit_id), - p.output, - ) - logging.info( - "Successfully built version {versionName} of {appid} from {commit_id}".format( - versionName=build.versionName, appid=app.id, commit_id=commit_id - ) - ) - - return p + return p, bindir -def collect_build_output(app, build, p, flavours_cmd): - root_dir = get_build_root_dir(app, build) - - omethod = build.output_method() - if omethod == 'maven': - stdout_apk = '\n'.join( - [ - line - for line in p.output.splitlines() - if any(a in line for a in ('.apk', '.ap_', '.jar')) - ] - ) - m = re.match( - r".*^\[INFO\] .*apkbuilder.*/([^/]*)\.apk", stdout_apk, re.S | re.M - ) - if not m: - m = re.match( - r".*^\[INFO\] Creating additional unsigned apk file .*/([^/]+)\.apk[^l]", - stdout_apk, - re.S | re.M, - ) - if not m: - m = re.match( - r'.*^\[INFO\] [^$]*aapt \[package,[^$]*' - + bindir - + r'/([^/]+)\.ap[_k][,\]]', - stdout_apk, - re.S | re.M, - ) - - if not m: - m = re.match( - r".*^\[INFO\] Building jar: .*/" + bindir + r"/(.+)\.jar", - stdout_apk, - re.S | re.M, - ) - if not m: - raise fdroidserver.exception.BuildException('Failed to find output') - src = m.group(1) - src = os.path.join(bindir, src) + '.apk' - - elif omethod == 'gradle': - src = None - apk_dirs = [ - # gradle plugin >= 3.0 - os.path.join(root_dir, 'build', 'outputs', 'apk', 'release'), - # gradle plugin < 3.0 and >= 0.11 - os.path.join(root_dir, 'build', 'outputs', 'apk'), - # really old path - os.path.join(root_dir, 'build', 'apk'), - ] - # If we build with gradle flavours with gradle plugin >= 3.0 the APK will be in - # a subdirectory corresponding to the flavour command used, but with different - # capitalization. - if flavours_cmd: - apk_dirs.append( - os.path.join( - root_dir, - 'build', - 'outputs', - 'apk', - transform_first_char(flavours_cmd, str.lower), - 'release', - ) - ) - for apks_dir in apk_dirs: - for apkglob in ['*-release-unsigned.apk', '*-unsigned.apk', '*.apk']: - apks = glob.glob(os.path.join(apks_dir, apkglob)) - - if len(apks) > 1: - raise fdroidserver.exception.BuildException( - 'More than one resulting apks found in %s' % apks_dir, - '\n'.join(apks), - ) - if len(apks) == 1: - src = apks[0] - break - if src is not None: - break - - if src is None: - raise fdroidserver.exception.BuildException('Failed to find any output apks') - - elif omethod == 'ant': - stdout_apk = '\n'.join( - [line for line in p.output.splitlines() if '.apk' in line] - ) - src = re.match( - r".*^.*Creating (.+) for release.*$.*", stdout_apk, re.S | re.M - ).group(1) - src = os.path.join(bindir, src) - elif omethod == 'raw': - output_path = fdroidserver.common.replace_build_vars(build.output, build) - globpath = os.path.join(root_dir, output_path) - apks = glob.glob(globpath) - if len(apks) > 1: - raise fdroidserver.exception.BuildException('Multiple apks match %s' % globpath, '\n'.join(apks)) - if len(apks) < 1: - raise fdroidserver.exception.BuildException('No apks match %s' % globpath) - src = os.path.normpath(apks[0]) - - -def buildbuildbuild___(app, build, config, gradletasks): - root_dir = get_build_root_dir(app, build) - - bmethod = build.build_method() - if bmethod == 'maven': - logging.info("Building Maven project...") - - if '@' in build.maven: - maven_dir = os.path.join(root_dir, build.maven.split('@', 1)[1]) - else: - maven_dir = root_dir - - mvncmd = [ - config['mvn3'], - '-Dandroid.sdk.path=' + config['sdk_path'], - '-Dmaven.jar.sign.skip=true', - '-Dmaven.test.skip=true', - '-Dandroid.sign.debug=false', - '-Dandroid.release=true', - 'package', - ] - if build.target: - target = build.target.split('-')[1] - fdroidserver.common.regsub_file( - r'[0-9]*', - r'%s' % target, - os.path.join(root_dir, 'pom.xml'), - ) - if '@' in build.maven: - fdroidserver.common.regsub_file( - r'[0-9]*', - r'%s' % target, - os.path.join(maven_dir, 'pom.xml'), - ) - - p = fdroidserver.common.FDroidPopen(mvncmd, cwd=maven_dir) - - bindir = os.path.join(root_dir, 'target') - - elif bmethod == 'gradle': - logging.info("Building Gradle project...") - - cmd = [config['gradle']] - if build.gradleprops: - cmd += ['-P' + kv for kv in build.gradleprops] - - cmd += gradletasks - - p = fdroidserver.common.FDroidPopen( - cmd, - cwd=root_dir, - envs={ - "GRADLE_VERSION_DIR": config['gradle_version_dir'], - "CACHEDIR": config['cachedir'], - }, - ) - - elif bmethod == 'ant': - logging.info("Building Ant project...") - cmd = ['ant'] - if build.antcommands: - cmd += build.antcommands - else: - cmd += ['release'] - p = fdroidserver.common.FDroidPopen(cmd, cwd=root_dir) - - bindir = os.path.join(root_dir, 'bin') - - -def check_build_success(app, build, p): - build_dir = fdroidserver.common.get_build_dir(app) - - if os.path.isdir(os.path.join(build_dir, '.git')): - import git - - commit_id = fdroidserver.common.get_head_commit_id(git.repo.Repo(build_dir)) - else: - commit_id = build.commit - - if p is not None and p.returncode != 0: - raise fdroidserver.exception.BuildException( - "Build failed for %s:%s@%s" % (app.id, build.versionName, commit_id), - p.output, - ) - logging.info( - "Successfully built version {versionName} of {appid} from {commit_id}".format( - versionName=build.versionName, appid=app.id, commit_id=commit_id - ) - ) - - -def collect_output_again(app, build, p, flavours_cmd): +def collect_build_output(app, build, p, flavours_cmd, bindir): root_dir = get_build_root_dir(app, build) omethod = build.output_method() @@ -640,7 +459,9 @@ def collect_output_again(app, build, p, flavours_cmd): break if src is None: - raise fdroidserver.exception.BuildException('Failed to find any output apks') + raise fdroidserver.exception.BuildException( + 'Failed to find any output apks' + ) elif omethod == 'ant': stdout_apk = '\n'.join( @@ -655,17 +476,39 @@ def collect_output_again(app, build, p, flavours_cmd): globpath = os.path.join(root_dir, output_path) apks = glob.glob(globpath) if len(apks) > 1: - raise fdroidserver.exception.BuildException('Multiple apks match %s' % globpath, '\n'.join(apks)) + raise fdroidserver.exception.BuildException( + 'Multiple apks match %s' % globpath, '\n'.join(apks) + ) if len(apks) < 1: raise fdroidserver.exception.BuildException('No apks match %s' % globpath) src = os.path.normpath(apks[0]) return src -def execute_postbuild(app, build, src): +def check_build_success(app, build, p): build_dir = fdroidserver.common.get_build_dir(app) + + if os.path.isdir(os.path.join(build_dir, '.git')): + import git + + commit_id = fdroidserver.common.get_head_commit_id(git.repo.Repo(build_dir)) + else: + commit_id = build.commit + + if p is not None and p.returncode != 0: + raise fdroidserver.exception.BuildException( + "Build failed for %s:%s@%s" % (app.id, build.versionName, commit_id), + p.output, + ) + logging.info( + "Successfully built version {versionName} of {appid} from {commit_id}".format( + versionName=build.versionName, appid=app.id, commit_id=commit_id + ) + ) + + +def execute_postbuild(app, build, src, srclibpaths): root_dir = get_build_root_dir(app, build) - srclibpaths = get_srclibpaths(build, build_dir) if build.postbuild: logging.info(f"Running 'postbuild' commands in {root_dir}") @@ -673,9 +516,9 @@ def execute_postbuild(app, build, src): # Substitute source library paths into commands... for name, number, libpath in srclibpaths: - cmd = cmd.replace(f"$${name}$$", str(Path.cwd() / libpath)) + cmd = cmd.replace(f"$${name}$$", str(pathlib.Path.cwd() / libpath)) - cmd = cmd.replace('$$OUT$$', str(Path(src).resolve())) + cmd = cmd.replace('$$OUT$$', str(pathlib.Path(src).resolve())) p = fdroidserver.common.FDroidPopen( ['bash', '-e', '-u', '-o', 'pipefail', '-x', '-c', cmd], cwd=root_dir @@ -722,14 +565,20 @@ def get_metadata_from_apk(app, build, apkfile): native_code = fdroidserver.common.get_native_code(apkfile) if build.buildjni and build.buildjni != ['no'] and not native_code: - raise fdroidserver.exception.BuildException("Native code should have been built but none was packaged") + raise fdroidserver.exception.BuildException( + "Native code should have been built but none was packaged" + ) if build.novcheck: versionCode = build.versionCode versionName = build.versionName if not versionCode or versionName is None: - raise fdroidserver.exception.BuildException("Could not find version information in build in output") + raise fdroidserver.exception.BuildException( + "Could not find version information in build in output" + ) if not appid: - raise fdroidserver.exception.BuildException("Could not find package ID in output") + raise fdroidserver.exception.BuildException( + "Could not find package ID in output" + ) if appid != app.id: raise fdroidserver.exception.BuildException( "Wrong package ID - build " + appid + " but expected " + app.id @@ -749,7 +598,9 @@ def validate_build_artifacts(app, build, src): # code in our new APK match what we expect... logging.debug("Checking " + src) if not os.path.exists(src): - raise fdroidserver.exception.BuildException("Unsigned APK is not at expected location of " + src) + raise fdroidserver.exception.BuildException( + "Unsigned APK is not at expected location of " + src + ) if fdroidserver.common.get_file_extension(src) == 'apk': vercode, version = get_metadata_from_apk(app, build, src) @@ -792,7 +643,7 @@ def run_this_build(config, options, package_name, version_code): # not sure if this makes any sense to change open file limits since we know # that this script will only ever build one app - rlimit_check(1) + rlimit_check() logging.info( "Building version %s (%s) of %s" @@ -801,7 +652,7 @@ def run_this_build(config, options, package_name, version_code): # init fdroid Popen wrapper fdroidserver.common.set_FDroidPopen_env(build) - p, gradletasks, flavours_cmd = init_build_subprocess(app, build, config) + gradletasks, flavours_cmd = init_build(app, build, config) sanitize_build_dir(app) @@ -814,20 +665,20 @@ def run_this_build(config, options, package_name, version_code): make_tarball(app, build, tmp_dir) # Run a build command if one is required... - execute_build_commands(app, build) + srclibpaths = checkout_srclibs(app, build) + execute_build_commands(app, build, srclibpaths) # Build native stuff if required... - execute_buildjni_commands(app, build) + ndk_path = install_ndk(build, config) # TODO: move to prepare step? + execute_buildjni_commands(app, build, ndk_path) # Build the release... - p = execute_build___(app, build, config, gradletasks) - collect_build_output(app, build, p, flavours_cmd) - buildbuildbuild___(app, build, config, gradletasks) + p, bindir = execute_build(app, build, config, gradletasks) check_build_success(app, build, p) - src = collect_output_again(app, build, p, flavours_cmd) + src = collect_build_output(app, build, p, flavours_cmd, bindir) # Run a postbuild command if one is required... - execute_postbuild(app, build, src) + execute_postbuild(app, build, src, srclibpaths) validate_build_artifacts(app, build, src) @@ -835,6 +686,7 @@ def run_this_build(config, options, package_name, version_code): move_build_output(app, build, src, tmp_dir) + def main(): parser = argparse.ArgumentParser( description=_( @@ -844,7 +696,8 @@ def main(): ) ) parser.add_argument( - "APP_VERSION", help=_("app id and version code tuple (e.g. org.fdroid.fdroid:1019051)") + "APP_VERSION", + help=_("app id and version code tuple (e.g. org.fdroid.fdroid:1019051)"), ) # fdroid args/opts boilerplate diff --git a/fdroidserver/build_local_sudo.py b/fdroidserver/build_local_sudo.py new file mode 100644 index 00000000..e878edae --- /dev/null +++ b/fdroidserver/build_local_sudo.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# build.py - part of the FDroid server tools +# Copyright (C) 2024, Michael Pöhn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +import os +import logging +import argparse + +from fdroidserver import _ +import fdroidserver.common + + +def sudo_run(app, build): + # before doing anything, run the sudo commands to setup the VM + if build.sudo: + logging.info("Running 'sudo' commands in %s" % os.getcwd()) + + p = fdroidserver.common.FDroidPopen( + [ + 'sudo', + 'DEBIAN_FRONTEND=noninteractive', + 'bash', + '-e', + '-u', + '-o', + 'pipefail', + '-x', + '-c', + '; '.join(build.sudo), + ] + ) + if p.returncode != 0: + raise fdroidserver.exception.BuildException( + "Error running sudo command for %s:%s" % (app.id, build.versionName), + p.output, + ) + + +def sudo_lock_root(app, build): + p = fdroidserver.common.FDroidPopen(['sudo', 'passwd', '--lock', 'root']) + if p.returncode != 0: + raise fdroidserver.exception.BuildException( + "Error locking root account for %s:%s" % (app.id, build.versionName), + p.output, + ) + + +def sudo_uninstall(app, build): + p = fdroidserver.common.FDroidPopen( + ['sudo', 'SUDO_FORCE_REMOVE=yes', 'dpkg', '--purge', 'sudo'] + ) + if p.returncode != 0: + raise fdroidserver.exception.BuildException( + "Error removing sudo for %s:%s" % (app.id, build.versionName), p.output + ) + + +def main(): + parser = argparse.ArgumentParser( + description=_( + """Run sudo commands """ + ), + ) + parser.add_argument( + "APP_VERSION", + help=_("app id and version code tuple 'APPID:VERCODE'"), + ) + + # fdroid args/opts boilerplate + fdroidserver.common.setup_global_opts(parser) + options = fdroidserver.common.parse_args(parser) + config = fdroidserver.common.get_config() + config # silcense pyflakes + + package_name, version_code = fdroidserver.common.split_pkg_arg(options.APP_VERSION) + app, build = fdroidserver.metadata.read_build_metadata(package_name, version_code) + + + # intialize FDroidPopen + # TODO: remove once FDroidPopen is replaced with vm/container exec + fdroidserver.common.set_FDroidPopen_env(build) + + # run sudo stuff + sudo_run(app, build) + sudo_lock_root(app, build) + sudo_uninstall(app, build) + +if __name__ == "__main__": + main() diff --git a/tests/build_local_run.TestCase b/tests/build_local_run.TestCase new file mode 100755 index 00000000..8fb90a1e --- /dev/null +++ b/tests/build_local_run.TestCase @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +import os +import sys +# import json +# import shutil +# import logging +import inspect +# import tempfile +import unittest +import unittest.mock + +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.build_local_run +import fdroidserver.metadata + + +class GetBuildRootDirTest(unittest.TestCase): + def test_no_subdir(self): + app = unittest.mock.MagicMock() + app.id = "fake.app" + build = unittest.mock.MagicMock() + build.subdir = None + + gbdmock = unittest.mock.Mock(return_value="ok") + with unittest.mock.patch("fdroidserver.common.get_build_dir", gbdmock): + r = fdroidserver.build_local_run.get_build_root_dir(app, build) + + gbdmock.assert_called_once_with(app) + self.assertEqual(r, "ok") + + def test_subdir(self): + app = unittest.mock.MagicMock() + app.id = "fake.app" + build = unittest.mock.MagicMock() + build.subdir = "abc/def" + + gbdmock = unittest.mock.Mock(return_value="ok") + with unittest.mock.patch("fdroidserver.common.get_build_dir", gbdmock): + r = fdroidserver.build_local_run.get_build_root_dir(app, build) + + gbdmock.assert_called_once_with(app) + self.assertEqual(r, "ok/abc/def") + + +if __name__ == "__main__": + os.chdir(os.path.dirname(__file__)) + + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument( + "-v", + "--verbose", + action="store_true", + default=False, + help="Spew out even more information than normal", + ) + fdroidserver.common.options = fdroidserver.common.parse_args(parser) + + newSuite = unittest.TestSuite() + newSuite.addTest(unittest.makeSuite(GetBuildRootDirTest)) + unittest.main(failfast=False)