1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-09-21 04:10:37 +02:00
This commit is contained in:
Michael Pöhn 2024-06-04 14:55:37 +02:00
parent ffa9e74d3f
commit 2f2a09fe14
No known key found for this signature in database
GPG Key ID: 725F386C05529A5A
4 changed files with 281 additions and 325 deletions

View File

@ -19,58 +19,13 @@
import os import os
import pathlib import pathlib
import logging # import logging
import argparse import argparse
from fdroidserver import _ from fdroidserver import _
import fdroidserver.common 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): def log_tools_version(app, build, log_dir):
log_path = os.path.join( log_path = os.path.join(
log_dir, fdroidserver.common.get_toolsversion_logname(app, build) log_dir, fdroidserver.common.get_toolsversion_logname(app, build)
@ -86,24 +41,6 @@ def main():
"for one specific build" "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( parser.add_argument(
"APP_VERSION", "APP_VERSION",
help=_("app id and version code tuple 'APPID:VERCODE'"), help=_("app id and version code tuple 'APPID:VERCODE'"),
@ -114,6 +51,8 @@ def main():
options = fdroidserver.common.parse_args(parser) options = fdroidserver.common.parse_args(parser)
config = fdroidserver.common.get_config() config = fdroidserver.common.get_config()
config # silence pyflakes
package_name, version_code = fdroidserver.common.split_pkg_arg(options.APP_VERSION) package_name, version_code = fdroidserver.common.split_pkg_arg(options.APP_VERSION)
app, build = fdroidserver.metadata.read_build_metadata(package_name, version_code) 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): for d in (srclib_dir, extlib_dir, log_dir, output_dir):
d.mkdir(exist_ok=True, parents=True) 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 # 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) log_tools_version(app, build, log_dir)
# do git/vcs checkout # do git/vcs checkout

View File

@ -1,8 +1,8 @@
import os import os
import re
import sys import sys
import glob import glob
import shutil import shutil
import gettext
import logging import logging
import pathlib import pathlib
import tarfile import tarfile
@ -16,8 +16,17 @@ import fdroidserver.exception
from fdroidserver import _ from fdroidserver import _
def rlimit_check(apps_count): def rlimit_check(apps_count=1):
"""make sure enough open files are allowed to process everything""" """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: try:
import resource # not available on Windows import resource # not available on Windows
@ -37,7 +46,18 @@ def rlimit_check(apps_count):
pass 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() ndk_path = build.ndk_path()
if build.ndk or (build.buildjni and build.buildjni != ['no']): if build.ndk or (build.buildjni and build.buildjni != ['no']):
if not ndk_path: if not ndk_path:
@ -47,13 +67,11 @@ def get_ndk_path(build):
if k.endswith("_orig"): if k.endswith("_orig"):
continue continue
logging.warning(" %s: %s" % (k, v)) logging.warning(" %s: %s" % (k, v))
if onserver: fdroidserver.common.auto_install_ndk(build)
fdroidserver.common.auto_install_ndk(build)
else:
raise fdroidserver.exception.FDroidException()
elif not os.path.isdir(ndk_path): elif not os.path.isdir(ndk_path):
logging.critical("Android NDK '%s' is not a directory!" % ndk_path) logging.critical("Android NDK '%s' is not a directory!" % ndk_path)
raise fdroidserver.exception.FDroidException() raise fdroidserver.exception.FDroidException()
return ndk_path
def get_build_root_dir(app, build): def get_build_root_dir(app, build):
@ -71,7 +89,7 @@ def transform_first_char(string, method):
return method(string[0]) + string[1:] 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 # We need to clean via the build tool in case the binary dirs are
# different from the default ones # different from the default ones
root_dir = get_build_root_dir(app, build) 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 "Error cleaning %s:%s" % (app.id, build.versionName), p.output
) )
return p, gradletasks, flavours_cmd return gradletasks, flavours_cmd
def sanitize_build_dir(app): 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) build_dir = fdroidserver.common.get_build_dir(app)
for root, dirs, files in os.walk(build_dir): for root, dirs, files in os.walk(build_dir):
@ -206,9 +235,8 @@ def make_tarball(app, build, tmp_dir):
tarball.close() tarball.close()
def execute_build_commands(app, build): def execute_build_commands(app, build, srclibpaths):
build_dir = fdroidserver.common.get_build_dir(app) root_dir = get_build_root_dir(app, build)
srclibpaths = get_srclibpaths(build, build_dir)
if build.build: if build.build:
logging.info("Running 'build' commands in %s" % root_dir) logging.info("Running 'build' commands in %s" % root_dir)
cmd = fdroidserver.common.replace_config_vars("; ".join(build.build), build) 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 = [] srclibpaths = []
if build.srclibs: if build.srclibs:
logging.info("Collecting source libraries") logging.info("Collecting source libraries")
for lib in build.srclibs: for lib in build.srclibs:
srclibpaths.append( srclibpaths.append(
fdroidserver.common.getsrclib( fdroidserver.common.getsrclib(
lib, srclib_dir, preponly=onserver, refresh=refresh, build=build lib, build_dir, prepare=False, refresh=False, build=build
) )
) )
return srclibpaths return srclibpaths
def execute_buildjni_commands(app, build): def execute_buildjni_commands(app, build, ndk_path):
root_dir = get_build_root_dir(app, build) root_dir = get_build_root_dir(app, build)
ndk_path = get_ndk_path(build)
if build.buildjni and build.buildjni != ['no']: if build.buildjni and build.buildjni != ['no']:
logging.info("Building the native code") logging.info("Building the native code")
jni_components = build.buildjni 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) build_dir = fdroidserver.common.get_build_dir(app)
root_dir = get_build_root_dir(app, build) root_dir = get_build_root_dir(app, build)
p = None p = None
bindir = None
bmethod = build.build_method() bmethod = build.build_method()
if bmethod == 'maven': if bmethod == 'maven':
logging.info("Building Maven project...") logging.info("Building Maven project...")
@ -344,221 +374,10 @@ def execute_build___(app, build, config, gradletasks):
bindir = os.path.join(root_dir, 'bin') bindir = os.path.join(root_dir, 'bin')
if os.path.isdir(os.path.join(build_dir, '.git')): return p, bindir
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
def collect_build_output(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()
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'<platform>[0-9]*</platform>',
r'<platform>%s</platform>' % target,
os.path.join(root_dir, 'pom.xml'),
)
if '@' in build.maven:
fdroidserver.common.regsub_file(
r'<platform>[0-9]*</platform>',
r'<platform>%s</platform>' % 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):
root_dir = get_build_root_dir(app, build) root_dir = get_build_root_dir(app, build)
omethod = build.output_method() omethod = build.output_method()
@ -640,7 +459,9 @@ def collect_output_again(app, build, p, flavours_cmd):
break break
if src is None: 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': elif omethod == 'ant':
stdout_apk = '\n'.join( 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) globpath = os.path.join(root_dir, output_path)
apks = glob.glob(globpath) apks = glob.glob(globpath)
if len(apks) > 1: 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: if len(apks) < 1:
raise fdroidserver.exception.BuildException('No apks match %s' % globpath) raise fdroidserver.exception.BuildException('No apks match %s' % globpath)
src = os.path.normpath(apks[0]) src = os.path.normpath(apks[0])
return src return src
def execute_postbuild(app, build, src): def check_build_success(app, build, p):
build_dir = fdroidserver.common.get_build_dir(app) 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) root_dir = get_build_root_dir(app, build)
srclibpaths = get_srclibpaths(build, build_dir)
if build.postbuild: if build.postbuild:
logging.info(f"Running 'postbuild' commands in {root_dir}") 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... # Substitute source library paths into commands...
for name, number, libpath in srclibpaths: 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( p = fdroidserver.common.FDroidPopen(
['bash', '-e', '-u', '-o', 'pipefail', '-x', '-c', cmd], cwd=root_dir ['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) native_code = fdroidserver.common.get_native_code(apkfile)
if build.buildjni and build.buildjni != ['no'] and not native_code: 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: if build.novcheck:
versionCode = build.versionCode versionCode = build.versionCode
versionName = build.versionName versionName = build.versionName
if not versionCode or versionName is None: 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: 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: if appid != app.id:
raise fdroidserver.exception.BuildException( raise fdroidserver.exception.BuildException(
"Wrong package ID - build " + appid + " but expected " + app.id "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... # code in our new APK match what we expect...
logging.debug("Checking " + src) logging.debug("Checking " + src)
if not os.path.exists(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': if fdroidserver.common.get_file_extension(src) == 'apk':
vercode, version = get_metadata_from_apk(app, build, src) 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 # not sure if this makes any sense to change open file limits since we know
# that this script will only ever build one app # that this script will only ever build one app
rlimit_check(1) rlimit_check()
logging.info( logging.info(
"Building version %s (%s) of %s" "Building version %s (%s) of %s"
@ -801,7 +652,7 @@ def run_this_build(config, options, package_name, version_code):
# init fdroid Popen wrapper # init fdroid Popen wrapper
fdroidserver.common.set_FDroidPopen_env(build) 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) sanitize_build_dir(app)
@ -814,20 +665,20 @@ def run_this_build(config, options, package_name, version_code):
make_tarball(app, build, tmp_dir) make_tarball(app, build, tmp_dir)
# Run a build command if one is required... # 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... # 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... # Build the release...
p = execute_build___(app, build, config, gradletasks) p, bindir = execute_build(app, build, config, gradletasks)
collect_build_output(app, build, p, flavours_cmd)
buildbuildbuild___(app, build, config, gradletasks)
check_build_success(app, build, p) 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... # 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) 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) move_build_output(app, build, src, tmp_dir)
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=_( description=_(
@ -844,7 +696,8 @@ def main():
) )
) )
parser.add_argument( 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 # fdroid args/opts boilerplate

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
#
# build.py - part of the FDroid server tools
# Copyright (C) 2024, Michael Pöhn <michael@poehn.at>
#
# 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 <http://www.gnu.org/licenses/>.
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()

69
tests/build_local_run.TestCase Executable file
View File

@ -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)