mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-10 17:30:11 +01: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_prepare",
|
||||||
"build_local_run",
|
"build_local_run",
|
||||||
"build_local_sudo",
|
"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