mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-09-21 04:10:37 +02:00
⏱️ add schedule_build subcommand
Internal subcommand for getting a (JSON) list of all apps that need building in the next cycle.
This commit is contained in:
parent
629fd1a204
commit
af1205f44c
@ -62,6 +62,7 @@ COMMANDS_INTERNAL = [
|
||||
"build_local_prepare",
|
||||
"build_local_run",
|
||||
"build_local_sudo",
|
||||
"schedule_build",
|
||||
]
|
||||
|
||||
|
||||
|
128
fdroidserver/schedule_build.py
Normal file
128
fdroidserver/schedule_build.py
Normal file
@ -0,0 +1,128 @@
|
||||
#!/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 sys
|
||||
import json
|
||||
import traceback
|
||||
import argparse
|
||||
|
||||
from fdroidserver import _
|
||||
import fdroidserver.common
|
||||
import fdroidserver.metadata
|
||||
import fdroidserver.update
|
||||
|
||||
|
||||
def is_binary_artifact_present(appid, build):
|
||||
"""Check if a build artifact/result form a previous run exists.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
appid
|
||||
app id you're looking for (e.g. 'org.fdroid.fdroid')
|
||||
build
|
||||
metadata build object you're checking
|
||||
|
||||
Returns
|
||||
-------
|
||||
True if a build artifact exists, otherwise False.
|
||||
"""
|
||||
bin_dirs = ["archive", "repo", "unsigned"]
|
||||
ext = get_output_extension(build)
|
||||
|
||||
for bin_dir in bin_dirs:
|
||||
if os.path.exists(f"./{bin_dir}/{appid}_{build.versionCode}.{ext}"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def collect_schedule_entries(apps):
|
||||
"""Get list of schedule entries for next build run.
|
||||
|
||||
This function matches which builds in metadata are not built yet.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
apps
|
||||
list of all metadata app objects of current repo
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of schedule entries
|
||||
"""
|
||||
schedule = []
|
||||
for appid, app in apps.items():
|
||||
if not app.get("Disabled"):
|
||||
for build in app.get("Builds", {}):
|
||||
if not build.get("disable"):
|
||||
if not is_binary_artifact_present(appid, build):
|
||||
schedule.append(
|
||||
{
|
||||
"applicationId": appid,
|
||||
"versionCode": build.get("versionCode"),
|
||||
"timeout": build.get("timeout"),
|
||||
}
|
||||
)
|
||||
return schedule
|
||||
|
||||
|
||||
# TODO remove this, and replace with this function from common.py
|
||||
def get_output_extension(build):
|
||||
if build.output:
|
||||
return fdroidserver.common.get_file_extension(
|
||||
fdroidserver.common.replace_build_vars(build.output, build)
|
||||
)
|
||||
return 'apk'
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=_(""""""),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pretty",
|
||||
'-p',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="pretty output formatting",
|
||||
)
|
||||
|
||||
# fdroid args/opts boilerplate
|
||||
fdroidserver.common.setup_global_opts(parser)
|
||||
options = fdroidserver.common.parse_args(parser)
|
||||
config = fdroidserver.common.get_config()
|
||||
config # silcense pyflakes
|
||||
|
||||
try:
|
||||
apps = fdroidserver.metadata.read_metadata()
|
||||
schedule = collect_schedule_entries(apps)
|
||||
|
||||
indent = 2 if options.pretty else None
|
||||
print(json.dumps(schedule, indent=indent))
|
||||
except Exception as e:
|
||||
if options.verbose:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
145
tests/schedule_build.TestCase
Executable file
145
tests/schedule_build.TestCase
Executable file
@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
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.schedule_build
|
||||
import fdroidserver.metadata
|
||||
import testcommon
|
||||
|
||||
|
||||
class IsBinaryArtifactPresentTest(unittest.TestCase):
|
||||
def mktestdirs(self):
|
||||
os.mkdir("unsigned")
|
||||
os.mkdir("archive")
|
||||
os.mkdir("repo")
|
||||
|
||||
def test_not_present(self):
|
||||
build = unittest.mock.Mock()
|
||||
build.output = None
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir, testcommon.TmpCwd(tmpdir):
|
||||
result = fdroidserver.schedule_build.is_binary_artifact_present(
|
||||
"fake.app", build
|
||||
)
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_present_in_repo(self):
|
||||
build = fdroidserver.metadata.Build()
|
||||
build["versionCode"] = 123
|
||||
build["output"] = None
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir, testcommon.TmpCwd(tmpdir):
|
||||
self.mktestdirs()
|
||||
with open('./repo/fake.app_123.apk', 'w') as f:
|
||||
f.write('')
|
||||
result = fdroidserver.schedule_build.is_binary_artifact_present(
|
||||
"fake.app", build
|
||||
)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_present_in_unsigned(self):
|
||||
build = fdroidserver.metadata.Build()
|
||||
build["versionCode"] = 123
|
||||
build["output"] = None
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir, testcommon.TmpCwd(tmpdir):
|
||||
self.mktestdirs()
|
||||
with open('./unsigned/fake.app_123.apk', 'w') as f:
|
||||
f.write('')
|
||||
result = fdroidserver.schedule_build.is_binary_artifact_present(
|
||||
"fake.app", build
|
||||
)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_present_in_archive(self):
|
||||
build = fdroidserver.metadata.Build()
|
||||
build["versionCode"] = 123
|
||||
build["output"] = None
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir, testcommon.TmpCwd(tmpdir):
|
||||
self.mktestdirs()
|
||||
with open('./archive/fake.app_123.apk', 'w') as f:
|
||||
f.write('')
|
||||
result = fdroidserver.schedule_build.is_binary_artifact_present(
|
||||
"fake.app", build
|
||||
)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_present_in_repo_with_output(self):
|
||||
build = fdroidserver.metadata.Build()
|
||||
build["versionCode"] = 9000
|
||||
build["versionName"] = "vvv"
|
||||
build["commit"] = "commit1"
|
||||
build["output"] = "blah/blah/build_result.zip"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir, testcommon.TmpCwd(tmpdir):
|
||||
self.mktestdirs()
|
||||
with open('./repo/fake.app_9000.zip', 'w') as f:
|
||||
f.write('')
|
||||
result = fdroidserver.schedule_build.is_binary_artifact_present(
|
||||
"fake.app", build
|
||||
)
|
||||
self.assertTrue(result)
|
||||
|
||||
|
||||
class CollectScheduleEntriesTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.apps = {
|
||||
"chat.hal": fdroidserver.metadata.App(),
|
||||
"chat.gpt": fdroidserver.metadata.App(),
|
||||
"microsoft.copilot": fdroidserver.metadata.App(),
|
||||
}
|
||||
|
||||
self.apps['chat.hal']["Builds"].append(fdroidserver.metadata.Build())
|
||||
self.apps['chat.hal']["Builds"][0]["versionCode"] = 9000
|
||||
|
||||
self.apps['chat.gpt']["Disabled"] = "this is jank!"
|
||||
self.apps['chat.gpt']["Builds"].append(fdroidserver.metadata.Build())
|
||||
self.apps['chat.gpt']["Builds"][0]["versionCode"] = 4
|
||||
|
||||
self.apps['microsoft.copilot']["Builds"].append(fdroidserver.metadata.Build())
|
||||
self.apps['microsoft.copilot']["Builds"][0]["versionCode"] = 3
|
||||
self.apps['microsoft.copilot']["Builds"][0]["disable"] = "also jank!"
|
||||
|
||||
def test_asdf(self):
|
||||
with unittest.mock.patch(
|
||||
"fdroidserver.schedule_build.is_binary_artifact_present", return_value=False
|
||||
):
|
||||
result = fdroidserver.schedule_build.collect_schedule_entries(self.apps)
|
||||
self.maxDiff = None
|
||||
self.assertListEqual(
|
||||
result,
|
||||
[{"applicationId": "chat.hal", "versionCode": 9000, "timeout": None}],
|
||||
)
|
||||
|
||||
|
||||
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(IsBinaryArtifactPresentTest))
|
||||
unittest.main(failfast=False)
|
Loading…
Reference in New Issue
Block a user