1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-08-15 10:50:09 +02:00

Merge branch 'write-status-json' into 'master'

write buildserver status as public JSON files

See merge request fdroid/fdroidserver!716
This commit is contained in:
Michael Pöhn 2020-02-19 14:21:23 +00:00
commit 28117f31d3
14 changed files with 316 additions and 61 deletions

View File

@ -173,10 +173,14 @@ The repository of older versions of applications from the main demo repository.
# 'bar.info:/var/www/fdroid',
# }
# Uncomment this option if you want to logs of builds and other processes to
# your repository server(s). Logs get published to all servers configured in
# 'serverwebroot'. The name scheme is: .../repo/$APPID_$VERCODE.log.gz
# Only logs from build-jobs running inside a buildserver VM are supported.
# When running fdroid processes on a remote server, it is possible to
# publish extra information about the status. Each fdroid sub-command
# can create repo/status/running.json when it starts, then a
# repo/status/<sub-command>.json when it completes. The builds logs
# and other processes will also get published, if they are running in
# a buildserver VM. The build logs name scheme is:
# .../repo/$APPID_$VERCODE.log.gz. These files are also pushed to all
# servers configured in 'serverwebroot'.
#
# deploy_process_logs = True

View File

@ -83,6 +83,7 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force):
buildserverid = subprocess.check_output(['vagrant', 'ssh', '-c',
'cat /home/vagrant/buildserverid'],
cwd='builder').strip().decode()
status_output['buildserverid'] = buildserverid
logging.debug(_('Fetched buildserverid from VM: {buildserverid}')
.format(buildserverid=buildserverid))
except Exception as e:
@ -912,6 +913,7 @@ config = None
buildserverid = None
fdroidserverid = None
start_timestamp = time.gmtime()
status_output = None
timeout_event = threading.Event()
@ -978,6 +980,8 @@ def main():
else:
also_check_dir = None
status_output = common.setup_status_output(start_timestamp)
repo_dir = 'repo'
build_dir = 'build'
@ -1029,6 +1033,8 @@ def main():
# Build applications...
failed_apps = {}
build_succeeded = []
status_output['failedBuilds'] = failed_apps
status_output['successfulBuilds'] = build_succeeded
# Only build for 36 hours, then stop gracefully.
endtime = time.time() + 36 * 60 * 60
max_build_time_reached = False
@ -1201,10 +1207,12 @@ def main():
except Exception as e:
logging.error("Error while attempting to publish build log: %s" % e)
common.write_running_status_json(status_output)
if timer:
timer.cancel() # kill the watchdog timer
if max_build_time_reached:
status_output['maxBuildTimeReached'] = True
logging.info("Stopping after global build timeout...")
break
@ -1263,6 +1271,8 @@ def main():
newpage = site.Pages['build']
newpage.save('#REDIRECT [[' + wiki_page_path + ']]', summary='Update redirect')
common.write_status_json(status_output, options.pretty)
# hack to ensure this exits, even is some threads are still running
common.force_exit()

View File

@ -543,6 +543,18 @@ def checkupdates_app(app):
raise FDroidException("Git commit failed")
def status_update_json(processed, failed):
"""Output a JSON file with metadata about this run"""
logging.debug(_('Outputting JSON'))
output = common.setup_status_output(start_timestamp)
if processed:
output['processed'] = processed
if failed:
output['failed'] = failed
common.write_status_json(output)
def update_wiki(gplaylog, locallog):
if config.get('wiki_server') and config.get('wiki_path'):
try:
@ -644,6 +656,8 @@ def main():
return
locallog = ''
processed = []
failed = dict()
for appid, app in apps.items():
if options.autoonly and app.AutoUpdateMode in ('None', 'Static'):
@ -656,13 +670,15 @@ def main():
try:
checkupdates_app(app)
processed.append(appid)
except Exception as e:
msg = _("...checkupdate failed for {appid} : {error}").format(appid=appid, error=e)
logging.error(msg)
locallog += msg + '\n'
failed[appid] = str(e)
update_wiki(None, locallog)
status_update_json(processed, failed)
logging.info(_("Finished"))

View File

@ -20,6 +20,7 @@
# common.py is imported by all modules, so do not import third-party
# libraries here as they will become a requirement for all commands.
import git
import io
import os
import sys
@ -47,7 +48,7 @@ except ImportError:
import xml.etree.ElementTree as XMLElementTree # nosec this is a fallback only
from binascii import hexlify
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from distutils.version import LooseVersion
from queue import Queue
from zipfile import ZipFile
@ -587,17 +588,13 @@ def read_app_args(appid_versionCode_pairs, allapps, allow_vercodes=False):
def get_extension(filename):
"""get name and extension of filename, with extension always lower case"""
base, ext = os.path.splitext(filename)
if not ext:
return base, ''
return base, ext.lower()[1:]
def has_extension(filename, ext):
_ignored, f_ext = get_extension(filename)
return ext == f_ext
publish_name_regex = re.compile(r"^(.+)_([0-9]+)\.(apk|zip)$")
@ -674,6 +671,66 @@ def get_build_dir(app):
return os.path.join('build', app.id)
class Encoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return sorted(obj)
return super().default(obj)
def setup_status_output(start_timestamp):
"""Create the common output dictionary for public status updates"""
output = {
'commandLine': sys.argv,
'startTimestamp': int(time.mktime(start_timestamp) * 1000),
'subcommand': sys.argv[0].split()[1],
}
if os.path.isdir('.git'):
git_repo = git.repo.Repo(os.getcwd())
output['fdroiddata'] = {
'commitId': get_head_commit_id(git_repo),
'isDirty': git_repo.is_dirty(),
}
fdroidserver_dir = os.path.dirname(sys.argv[0])
if os.path.isdir(os.path.join(fdroidserver_dir, '.git')):
git_repo = git.repo.Repo(fdroidserver_dir)
output['fdroidserver'] = {
'commitId': get_head_commit_id(git_repo),
'isDirty': git_repo.is_dirty(),
}
write_running_status_json(output)
return output
def write_running_status_json(output):
write_status_json(output, pretty=True, name='running')
def write_status_json(output, pretty=False, name=None):
"""Write status out as JSON, and rsync it to the repo server"""
status_dir = os.path.join('repo', 'status')
if not os.path.exists(status_dir):
os.mkdir(status_dir)
if not name:
output['endTimestamp'] = int(datetime.now(timezone.utc).timestamp() * 1000)
name = sys.argv[0].split()[1] # fdroid subcommand
path = os.path.join(status_dir, name + '.json')
with open(path, 'w') as fp:
if pretty:
json.dump(output, fp, sort_keys=True, cls=Encoder, indent=2)
else:
json.dump(output, fp, sort_keys=True, cls=Encoder, separators=(',', ':'))
rsync_status_file_to_repo(path, repo_subdir='status')
def get_head_commit_id(git_repo):
"""Get git commit ID for HEAD as a str
repo.head.commit.binsha is a bytearray stored in a str
"""
return hexlify(bytearray(git_repo.head.commit.binsha)).decode()
def setup_vcs(app):
'''checkout code from VCS and return instance of vcs and the build dir'''
build_dir = get_build_dir(app)
@ -1316,7 +1373,7 @@ def manifest_paths(app_dir, flavours):
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):
if not path.endswith('.xml') or not os.path.isfile(path):
continue
logging.debug("fetch_real_name: Checking manifest at " + path)
xml = parse_xml(path)
@ -1808,11 +1865,11 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
for path in manifest_paths(root_dir, flavours):
if not os.path.isfile(path):
continue
if has_extension(path, 'xml'):
if path.endswith('.xml'):
regsub_file(r'android:versionName="[^"]*"',
r'android:versionName="%s"' % build.versionName,
path)
elif has_extension(path, 'gradle'):
elif path.endswith('.gradle'):
regsub_file(r"""(\s*)versionName[\s'"=]+.*""",
r"""\1versionName '%s'""" % build.versionName,
path)
@ -1822,11 +1879,11 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
for path in manifest_paths(root_dir, flavours):
if not os.path.isfile(path):
continue
if has_extension(path, 'xml'):
if path.endswith('.xml'):
regsub_file(r'android:versionCode="[^"]*"',
r'android:versionCode="%s"' % build.versionCode,
path)
elif has_extension(path, 'gradle'):
elif path.endswith('.gradle'):
regsub_file(r'versionCode[ =]+[0-9]+',
r'versionCode %s' % build.versionCode,
path)
@ -3300,11 +3357,6 @@ def deploy_build_log_with_rsync(appid, vercode, log_content):
be decoded as 'utf-8')
"""
# check if deploying logs is enabled in config
if not config.get('deploy_process_logs', False):
logging.debug(_('skip deploying full build logs: not enabled in config'))
return
if not log_content:
logging.warning(_('skip deploying full build logs: log content is empty'))
return
@ -3322,13 +3374,17 @@ def deploy_build_log_with_rsync(appid, vercode, log_content):
f.write(bytes(log_content, 'utf-8'))
else:
f.write(log_content)
rsync_status_file_to_repo(log_gz_path)
# TODO: sign compressed log file, if a signing key is configured
def rsync_status_file_to_repo(path, repo_subdir=None):
"""Copy a build log or status JSON to the repo using rsync"""
if not config.get('deploy_process_logs', False):
logging.debug(_('skip deploying full build logs: not enabled in config'))
return
for webroot in config.get('serverwebroot', []):
dest_path = os.path.join(webroot, "repo")
if not dest_path.endswith('/'):
dest_path += '/' # make sure rsync knows this is a directory
cmd = ['rsync',
'--archive',
'--delete-after',
@ -3339,15 +3395,21 @@ def deploy_build_log_with_rsync(appid, vercode, log_content):
cmd += ['--quiet']
if 'identity_file' in config:
cmd += ['-e', 'ssh -oBatchMode=yes -oIdentitiesOnly=yes -i ' + config['identity_file']]
cmd += [log_gz_path, dest_path]
# TODO: also deploy signature file if present
dest_path = os.path.join(webroot, "repo")
if repo_subdir is not None:
dest_path = os.path.join(dest_path, repo_subdir)
if not dest_path.endswith('/'):
dest_path += '/' # make sure rsync knows this is a directory
cmd += [path, dest_path]
retcode = subprocess.call(cmd)
if retcode:
logging.warning(_("failed deploying build logs to '{path}'").format(path=webroot))
logging.error(_('process log deploy {path} to {dest} failed!')
.format(path=path, dest=webroot))
else:
logging.info(_("deployed build logs to '{path}'").format(path=webroot))
logging.debug(_('deployed process log {path} to {dest}')
.format(path=path, dest=webroot))
def get_per_app_repos():

View File

@ -20,6 +20,7 @@ import os
import glob
from argparse import ArgumentParser
import logging
import time
from . import _
from . import common
@ -28,6 +29,17 @@ from .exception import FDroidException
config = None
options = None
start_timestamp = time.gmtime()
def status_update_json(signed):
"""Output a JSON file with metadata about this run"""
logging.debug(_('Outputting JSON'))
output = common.setup_status_output(start_timestamp)
if signed:
output['signed'] = signed
common.write_status_json(output)
def main():
@ -45,6 +57,7 @@ def main():
if config['archive_older'] != 0:
repodirs.append('archive')
signed = []
for output_dir in repodirs:
if not os.path.isdir(output_dir):
raise FDroidException(_("Missing output directory") + " '" + output_dir + "'")
@ -72,7 +85,9 @@ def main():
if p.returncode != 0:
raise FDroidException("Signing failed.")
signed.append(filename)
logging.info('Signed ' + filename)
status_update_json(signed)
if __name__ == "__main__":

View File

@ -17,12 +17,13 @@
# 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 binascii
import git
import glob
import json
import os
import re
import shutil
import sys
import urllib.parse
import urllib.request
import yaml
@ -230,7 +231,7 @@ def main():
apps = metadata.read_metadata()
app = None
build_dir = None
tmp_importer_dir = None
local_metadata_files = common.get_local_metadata_files()
if local_metadata_files != []:
@ -241,35 +242,34 @@ def main():
app = metadata.App()
app.AutoName = os.path.basename(os.getcwd())
app.RepoType = 'git'
app.UpdateCheckMode = "Tags"
if os.path.exists('build.gradle') or os.path.exists('build.gradle.kts'):
build.gradle = ['yes']
import git
repo = git.repo.Repo(os.getcwd()) # git repo
for remote in git.Remote.iter_items(repo):
git_repo = git.repo.Repo(os.getcwd())
for remote in git.Remote.iter_items(git_repo):
if remote.name == 'origin':
url = repo.remotes.origin.url
url = git_repo.remotes.origin.url
if url.startswith('https://git'): # github, gitlab
app.SourceCode = url.rstrip('.git')
app.Repo = url
break
# repo.head.commit.binsha is a bytearray stored in a str
build.commit = binascii.hexlify(bytearray(repo.head.commit.binsha))
write_local_file = True
elif options.url:
app = get_app_from_url(options.url)
build_dir = clone_to_tmp_dir(app)
build.commit = '?'
tmp_importer_dir = clone_to_tmp_dir(app)
git_repo = git.repo.Repo(tmp_importer_dir)
build.disable = 'Generated by import.py - check/set version fields and commit id'
write_local_file = False
else:
raise FDroidException("Specify project url.")
app.UpdateCheckMode = 'Tags'
build.commit = common.get_head_commit_id(git_repo)
# Extract some information...
paths = get_all_gradle_and_manifests(build_dir)
subdir = get_gradle_subdir(build_dir, paths)
paths = get_all_gradle_and_manifests(tmp_importer_dir)
subdir = get_gradle_subdir(tmp_importer_dir, paths)
if paths:
versionName, versionCode, package = common.parse_androidmanifests(paths, app)
if not package:
@ -303,8 +303,8 @@ def main():
or os.path.exists(os.path.join(subdir, 'build.gradle')):
build.gradle = ['yes']
package_json = os.path.join(build_dir, 'package.json') # react-native
pubspec_yaml = os.path.join(build_dir, 'pubspec.yaml') # flutter
package_json = os.path.join(tmp_importer_dir, 'package.json') # react-native
pubspec_yaml = os.path.join(tmp_importer_dir, 'pubspec.yaml') # flutter
if os.path.exists(package_json):
build.sudo = ['apt-get install npm', 'npm install -g react-native-cli']
build.init = ['npm install']
@ -314,7 +314,7 @@ def main():
app.License = data.get('license', app.License)
app.Description = data.get('description', app.Description)
app.WebSite = data.get('homepage', app.WebSite)
app_json = os.path.join(build_dir, 'app.json')
app_json = os.path.join(tmp_importer_dir, 'app.json')
if os.path.exists(app_json):
with open(app_json) as fp:
data = json.load(fp)
@ -343,8 +343,13 @@ def main():
# Keep the repo directory to save bandwidth...
if not os.path.exists('build'):
os.mkdir('build')
if build_dir is not None:
shutil.move(build_dir, os.path.join('build', package))
build_dir = os.path.join('build', package)
if os.path.exists(build_dir):
logging.warning(_('{path} already exists, ignoring import results!')
.format(path=build_dir))
sys.exit(1)
elif tmp_importer_dir is not None:
shutil.move(tmp_importer_dir, build_dir)
with open('build/.fdroidvcs-' + package, 'w') as f:
f.write(app.RepoType + ' ' + app.Repo)

View File

@ -28,6 +28,7 @@ from collections import OrderedDict
import logging
from gettext import ngettext
import json
import time
import zipfile
from . import _
@ -38,6 +39,7 @@ from .exception import BuildException, FDroidException
config = None
options = None
start_timestamp = time.gmtime()
def publish_source_tarball(apkfilename, unsigned_dir, output_dir):
@ -138,6 +140,20 @@ def store_stats_fdroid_signing_key_fingerprints(appids, indent=None):
sign_sig_key_fingerprint_list(jar_file)
def status_update_json(newKeyAliases, generatedKeys, signedApks):
"""Output a JSON file with metadata about this run"""
logging.debug(_('Outputting JSON'))
output = common.setup_status_output(start_timestamp)
if newKeyAliases:
output['newKeyAliases'] = newKeyAliases
if generatedKeys:
output['generatedKeys'] = generatedKeys
if signedApks:
output['signedApks'] = signedApks
common.write_status_json(output)
def main():
global config, options
@ -195,6 +211,9 @@ def main():
# collisions, and refuse to do any publishing if that's the case...
allapps = metadata.read_metadata()
vercodes = common.read_pkg_args(options.appid, True)
signed_apks = dict()
new_key_aliases = []
generated_keys = dict()
allaliases = []
for appid in allapps:
m = hashlib.md5() # nosec just used to generate a keyalias
@ -314,6 +333,7 @@ def main():
m = hashlib.md5() # nosec just used to generate a keyalias
m.update(appid.encode('utf-8'))
keyalias = m.hexdigest()[:8]
new_key_aliases.append(keyalias)
logging.info("Key alias: " + keyalias)
# See if we already have a key for this application, and
@ -336,6 +356,9 @@ def main():
'-dname', config['keydname']], envs=env_vars)
if p.returncode != 0:
raise BuildException("Failed to generate key", p.output)
if appid not in generated_keys:
generated_keys[appid] = set()
generated_keys[appid].add(appid)
signed_apk_path = os.path.join(output_dir, apkfilename)
if os.path.exists(signed_apk_path):
@ -353,6 +376,9 @@ def main():
apkfile, keyalias], envs=env_vars)
if p.returncode != 0:
raise BuildException(_("Failed to sign application"), p.output)
if appid not in signed_apks:
signed_apks[appid] = []
signed_apks[appid].append(apkfile)
# Zipalign it...
common._zipalign(apkfile, os.path.join(output_dir, apkfilename))
@ -362,6 +388,7 @@ def main():
logging.info('Published ' + apkfilename)
store_stats_fdroid_signing_key_fingerprints(allapps.keys())
status_update_json(new_key_aliases, generated_keys, signed_apks)
logging.info('published list signing-key fingerprints')

View File

@ -809,6 +809,7 @@ def main():
if config.get('wiki_server') and config.get('wiki_path'):
update_wiki()
common.write_status_json(common.setup_status_output(start_timestamp))
sys.exit(0)

View File

@ -17,6 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import time
import zipfile
from argparse import ArgumentParser
import logging
@ -27,6 +28,7 @@ from .exception import FDroidException
config = None
options = None
start_timestamp = time.gmtime()
def sign_jar(jar):
@ -75,6 +77,16 @@ def sign_index_v1(repodir, json_name):
sign_jar(jar_file)
def status_update_json(signed):
"""Output a JSON file with metadata about this run"""
logging.debug(_('Outputting JSON'))
output = common.setup_status_output(start_timestamp)
if signed:
output['signed'] = signed
common.write_status_json(output)
def main():
global config, options
@ -94,7 +106,7 @@ def main():
if config['archive_older'] != 0:
repodirs.append('archive')
signed = 0
signed = []
for output_dir in repodirs:
if not os.path.isdir(output_dir):
raise FDroidException("Missing output directory '" + output_dir + "'")
@ -102,9 +114,10 @@ def main():
unsigned = os.path.join(output_dir, 'index_unsigned.jar')
if os.path.exists(unsigned):
sign_jar(unsigned)
os.rename(unsigned, os.path.join(output_dir, 'index.jar'))
index_jar = os.path.join(output_dir, 'index.jar')
os.rename(unsigned, index_jar)
logging.info('Signed index in ' + output_dir)
signed += 1
signed.append(index_jar)
json_name = 'index-v1.json'
index_file = os.path.join(output_dir, json_name)
@ -112,10 +125,11 @@ def main():
sign_index_v1(output_dir, json_name)
os.remove(index_file)
logging.info('Signed ' + index_file)
signed += 1
signed.append(index_file)
if signed == 0:
if not signed:
logging.info(_("Nothing to do"))
status_update_json(signed)
if __name__ == "__main__":

View File

@ -121,6 +121,57 @@ def disabled_algorithms_allowed():
return options.allow_disabled_algorithms or config['allow_disabled_algorithms']
def status_update_json(apps, sortedids, apks):
"""Output a JSON file with metadata about this `fdroid update` run
:param apps: fully populated list of all applications
:param apks: all to be published apks
"""
logging.debug(_('Outputting JSON'))
output = common.setup_status_output(start_timestamp)
output['antiFeatures'] = dict()
output['disabled'] = []
output['failedBuilds'] = dict()
output['noPackages'] = []
for appid in sortedids:
app = apps[appid]
for af in app.get('AntiFeatures', []):
antiFeatures = output['antiFeatures'] # JSON camelCase
if af not in antiFeatures:
antiFeatures[af] = dict()
if appid not in antiFeatures[af]:
antiFeatures[af]['apps'] = set()
antiFeatures[af]['apps'].add(appid)
apklist = []
for apk in apks:
if apk['packageName'] == appid:
apklist.append(apk)
builds = app.get('builds', [])
validapks = 0
for build in builds:
if not build.get('disable'):
builtit = False
for apk in apklist:
if apk['versionCode'] == int(build.versionCode):
builtit = True
validapks += 1
break
if not builtit:
failedBuilds = output['failedBuilds']
if appid not in failedBuilds:
failedBuilds[appid] = []
failedBuilds[appid].append(build.versionCode)
if validapks == 0:
output['noPackages'].append(appid)
if app.get('Disabled'):
output['disabled'].append(appid)
common.write_status_json(output, options.pretty)
def update_wiki(apps, sortedids, apks):
"""Update the wiki
@ -2200,6 +2251,7 @@ def main():
# Update the wiki...
if options.wiki:
update_wiki(apps, sortedids, apks + archapks)
status_update_json(apps, sortedids, apks + archapks)
logging.info(_("Finished"))

View File

@ -64,13 +64,6 @@ class Decoder(json.JSONDecoder):
return set(values), end
class Encoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return sorted(obj)
return super().default(obj)
def write_json_report(url, remote_apk, unsigned_apk, compare_result):
"""write out the results of the verify run to JSON
@ -118,7 +111,7 @@ def write_json_report(url, remote_apk, unsigned_apk, compare_result):
data['packages'][packageName] = set()
data['packages'][packageName].add(output)
with open(jsonfile, 'w') as fp:
json.dump(data, fp, cls=Encoder, sort_keys=True)
json.dump(data, fp, cls=common.Encoder, sort_keys=True)
def main():

View File

@ -55,6 +55,7 @@ fi
gpg --import $GNUPGHOME/secring.gpg
echo "build_server_always = True" >> config.py
echo "deploy_process_logs = True" >> config.py
echo "make_current_version_link = False" >> config.py
echo "gpghome = '$GNUPGHOME'" >> config.py
echo "gpgkey = 'CE71F7FB'" >> config.py
@ -66,6 +67,7 @@ test -d repo || mkdir repo
test -d archive || mkdir archive
# when everything is copied over to run on SIGN machine
../fdroid publish
../fdroid gpgsign
# when everything is copied over to run on BUILD machine,
# which does not have a keyring, only a cached pubkey

View File

@ -5,13 +5,16 @@
import difflib
import glob
import inspect
import json
import logging
import optparse
import os
import re
import shutil
import subprocess
import sys
import tempfile
import time
import unittest
import textwrap
import yaml
@ -1131,6 +1134,55 @@ class CommonTest(unittest.TestCase):
with gzip.open(expected_log_path, 'r') as f:
self.assertEqual(f.read(), mocklogcontent)
def test_deploy_status_json(self):
testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
fakesubcommand = 'fakesubcommand'
fake_timestamp = 1234567890
fakeserver = 'example.com:/var/www/fbot/'
expected_dir = os.path.join(testdir, fakeserver.replace(':', ''), 'repo', 'status')
fdroidserver.common.options = mock.Mock()
fdroidserver.common.config = {}
fdroidserver.common.config['serverwebroot'] = [fakeserver]
fdroidserver.common.config['identity_file'] = 'ssh/id_rsa'
def assert_subprocess_call(cmd):
dest_path = os.path.join(testdir, cmd[-1].replace(':', ''))
if not os.path.exists(dest_path):
os.makedirs(dest_path)
return subprocess.run(cmd[:-1] + [dest_path]).returncode
with mock.patch('subprocess.call', side_effect=assert_subprocess_call):
with mock.patch.object(sys, 'argv', ['fdroid ' + fakesubcommand]):
output = fdroidserver.common.setup_status_output(time.localtime(fake_timestamp))
self.assertFalse(os.path.exists(os.path.join(expected_dir, 'running.json')))
with mock.patch.object(sys, 'argv', ['fdroid ' + fakesubcommand]):
fdroidserver.common.write_status_json(output)
self.assertFalse(os.path.exists(os.path.join(expected_dir, fakesubcommand + '.json')))
fdroidserver.common.config['deploy_process_logs'] = True
output = fdroidserver.common.setup_status_output(time.localtime(fake_timestamp))
expected_path = os.path.join(expected_dir, 'running.json')
self.assertTrue(os.path.isfile(expected_path))
with open(expected_path) as fp:
data = json.load(fp)
self.assertEqual(fake_timestamp * 1000, data['startTimestamp'])
self.assertFalse('endTimestamp' in data)
testvalue = 'asdfasd'
output['testvalue'] = testvalue
fdroidserver.common.write_status_json(output)
expected_path = os.path.join(expected_dir, fakesubcommand + '.json')
self.assertTrue(os.path.isfile(expected_path))
with open(expected_path) as fp:
data = json.load(fp)
self.assertEqual(fake_timestamp * 1000, data['startTimestamp'])
self.assertTrue('endTimestamp' in data)
self.assertEqual(testvalue, output.get('testvalue'))
def test_string_is_integer(self):
self.assertTrue(fdroidserver.common.string_is_integer('0x10'))
self.assertTrue(fdroidserver.common.string_is_integer('010'))

View File

@ -19,6 +19,7 @@ import sys
import unittest
import tempfile
import textwrap
from unittest import mock
localmodule = os.path.realpath(
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
@ -158,7 +159,8 @@ class PublishTest(unittest.TestCase):
os.path.join(testdir, 'unsigned', 'binaries', 'com.politedroid_6.binary.apk'))
os.chdir(testdir)
publish.main()
with mock.patch.object(sys, 'argv', ['fdroid fakesubcommand']):
publish.main()
if __name__ == "__main__":