diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index 0aa95d68..d158e1d6 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import git import os import re import urllib.request @@ -683,6 +684,46 @@ def get_last_build_from_app(app: metadata.App) -> metadata.Build: return metadata.Build() +def push_commits(remote_name='origin'): + """Push commits using either appid or 'checkupdates' as branch name.""" + git_repo = git.Repo.init('.') + files = set() + upstream_main = 'main' if 'main' in git_repo.remotes.upstream.refs else 'master' + local_main = 'main' if 'main' in git_repo.refs else 'master' + for commit in git_repo.iter_commits(f'upstream/{upstream_main}...{local_main}'): + files.update(commit.stats.files.keys()) + + branch_name = 'checkupdates' + files = list(files) + if len(files) == 1: + m = re.match(r'metadata/([^\s]+)\.yml', files[0]) + if m: + branch_name = m.group(1) # appid + if len(files) > 0: + git_repo.create_head(branch_name, force=True) + remote = git_repo.remotes[remote_name] + pushinfos = remote.push( + branch_name, force=True, set_upstream=True, progress=progress + ) + for pushinfo in pushinfos: + if pushinfo.flags & ( + git.remote.PushInfo.ERROR + | git.remote.PushInfo.REJECTED + | git.remote.PushInfo.REMOTE_FAILURE + | git.remote.PushInfo.REMOTE_REJECTED + ): + # Show potentially useful messages from git remote + if progress: + for line in progress.other_lines: + if line.startswith('remote:'): + logging.debug(line) + raise FDroidException( + f'{remote.url} push failed: {pushinfo.flags} {pushinfo.summary}' + ) + else: + logging.debug(remote.url + ': ' + pushinfo.summary) + + def status_update_json(processed: list, failed: dict) -> None: """Output a JSON file with metadata about this run.""" logging.debug(_('Outputting JSON')) diff --git a/tests/checkupdates.TestCase b/tests/checkupdates.TestCase index 9ff7eb98..8399695d 100755 --- a/tests/checkupdates.TestCase +++ b/tests/checkupdates.TestCase @@ -2,9 +2,13 @@ # http://www.drdobbs.com/testing/unit-testing-with-python/240165163 +import git import logging import os +import shutil import sys +import tempfile +import time import unittest from unittest import mock from pathlib import Path @@ -27,6 +31,12 @@ class CheckupdatesTest(unittest.TestCase): logging.basicConfig(level=logging.DEBUG) self.basedir = localmodule / 'tests' os.chdir(self.basedir) + self.testdir = tempfile.TemporaryDirectory( + str(time.time()), self._testMethodName + '_' + ) + + def tearDown(self): + self.testdir.cleanup() def test_autoupdatemode_no_suffix(self): fdroidserver.checkupdates.config = {} @@ -317,6 +327,56 @@ class CheckupdatesTest(unittest.TestCase): self.assertEqual(vername, '2') self.assertEqual(vercode, 2) + def test_push_commits(self): + testdir = self.testdir.name + os.chdir(testdir) + os.mkdir('metadata') + for f in (self.basedir / 'metadata').glob('*.yml'): + shutil.copy(f, 'metadata') + git_repo = git.Repo.init(testdir) + git_repo.git.add(all=True) + git_repo.index.commit("all metadata files") + + git_remote_upstream = os.path.join(testdir, 'git_remote_upstream') + upstream = git.Repo.init(git_remote_upstream, bare=True) + git_repo.create_remote('upstream', 'file://' + git_remote_upstream) + git_remote_origin = os.path.join(testdir, 'git_remote_origin') + origin = git.Repo.init(git_remote_origin, bare=True) + git_repo.create_remote('origin', 'file://' + git_remote_origin) + for remote in git_repo.remotes: + remote.push(git_repo.active_branch) + self.assertEqual(git_repo.head, upstream.head) + self.assertEqual(origin.head, upstream.head) + # pretend that checkupdates ran but didn't create any new commits + fdroidserver.checkupdates.push_commits() + + appid = 'org.adaway' + self.assertNotIn(appid, git_repo.branches) + self.assertNotIn('checkupdates', git_repo.branches) + self.assertNotIn(appid, git_repo.remotes.origin.repo.branches) # TODO fix + + # now make commit + app = fdroidserver.metadata.read_metadata({appid: -1})[appid] + build = fdroidserver.metadata.Build() + build.versionName = 'fake' + build.versionCode = 999999999 + app.Builds.append(build) + metadata_file = 'metadata/%s.yml' % appid + fdroidserver.metadata.write_metadata(metadata_file, app) + git_repo.index.add(metadata_file) + git_repo.index.commit('changed ' + appid) + + # and push the new commit to the dynamic branch + fdroidserver.checkupdates.push_commits() + self.assertIn(appid, git_repo.branches) + self.assertIn(appid, git_repo.remotes.origin.refs) + self.assertNotIn('checkupdates', git_repo.branches) + self.assertNotIn(appid, git_repo.remotes.upstream.refs) + + def test_make_merge_request(self): + testdir = self.testdir.name + os.chdir(testdir) + if __name__ == "__main__": import argparse