install: download from GitHub Releases

This commit is contained in:
Hans-Christoph Steiner 2024-10-22 00:12:43 +02:00
parent 1eb6516f16
commit 560472e4e5
3 changed files with 49 additions and 7 deletions

View file

@ -23,12 +23,15 @@ import urllib.parse
class GithubApi: class GithubApi:
""" """Wrapper for some select calls to GitHub Json/REST API.
Warpper for some select calls to GitHub Json/REST API.
This class wraps some calls to api.github.com. This is not intended to be a This class wraps some calls to api.github.com. This is not intended to be a
general API wrapper. Instead it's purpose is to return pre-filtered and general API wrapper. Instead it's purpose is to return pre-filtered and
transformed data that's playing well with other fdroidserver functions. transformed data that's playing well with other fdroidserver functions.
With the GitHub API, the token is optional, but it has pretty
severe rate limiting.
""" """
def __init__(self, api_token, repo_path): def __init__(self, api_token, repo_path):
@ -41,9 +44,10 @@ class GithubApi:
def _req(self, url, data=None): def _req(self, url, data=None):
h = { h = {
"Accept": "application/vnd.github+json", "Accept": "application/vnd.github+json",
"Authorization": f"Bearer {self._api_token}",
"X-GitHub-Api-Version": "2022-11-28", "X-GitHub-Api-Version": "2022-11-28",
} }
if self._api_token:
h["Authorization"] = f"Bearer {self._api_token}"
return urllib.request.Request( return urllib.request.Request(
url, url,
headers=h, headers=h,
@ -65,6 +69,17 @@ class GithubApi:
released_tags = self.list_released_tags() released_tags = self.list_released_tags()
return [x for x in all_tags if x not in released_tags] return [x for x in all_tags if x not in released_tags]
def get_latest_apk(self):
req = self._req(
f"https://api.github.com/repos/{self._repo_path}/releases/latest"
)
with urllib.request.urlopen(req) as resp: # nosec CWE-22 disable bandit warning
assets = json.load(resp)['assets']
for asset in assets:
url = asset.get('browser_download_url')
if url and url.endswith('.apk'):
return url
def tag_exists(self, tag): def tag_exists(self, tag):
""" """
Check if git tag is present on github. Check if git tag is present on github.

View file

@ -30,7 +30,7 @@ from pathlib import Path
from urllib.parse import urlencode, urlparse, urlunparse from urllib.parse import urlencode, urlparse, urlunparse
from . import _ from . import _
from . import common, index, net from . import common, github, index, net
from .exception import FDroidException from .exception import FDroidException
@ -106,6 +106,17 @@ def download_fdroid_apk(privacy_mode=False): # pylint: disable=unused-argument
return net.download_using_mirrors([mirror]) return net.download_using_mirrors([mirror])
def download_fdroid_apk_from_github(privacy_mode=False):
"""Download F-Droid.apk from F-Droid's GitHub Releases."""
if common.config and not privacy_mode:
token = common.config.get('github_token')
else:
token = None
gh = github.GithubApi(token, 'https://github.com/f-droid/fdroidclient')
latest_apk = gh.get_latest_apk()
return net.download_file(latest_apk)
def download_fdroid_apk_from_ipns(privacy_mode=False): def download_fdroid_apk_from_ipns(privacy_mode=False):
"""Download the F-Droid APK from an IPNS repo.""" """Download the F-Droid APK from an IPNS repo."""
cid = 'k51qzi5uqu5dl4hbcksbdmplanu9n4hivnqsupqe6vzve1pdbeh418ssptldd3' cid = 'k51qzi5uqu5dl4hbcksbdmplanu9n4hivnqsupqe6vzve1pdbeh418ssptldd3'
@ -161,11 +172,13 @@ def install_fdroid_apk(privacy_mode=False):
download_methods = [ download_methods = [
download_fdroid_apk_from_maven, download_fdroid_apk_from_maven,
download_fdroid_apk_from_ipns, download_fdroid_apk_from_ipns,
download_fdroid_apk_from_github,
] ]
else: else:
download_methods = [ download_methods = [
download_apk, download_apk,
download_fdroid_apk_from_maven, download_fdroid_apk_from_maven,
download_fdroid_apk_from_github,
download_fdroid_apk_from_ipns, download_fdroid_apk_from_ipns,
download_fdroid_apk, download_fdroid_apk,
] ]

View file

@ -177,56 +177,65 @@ class InstallTest(unittest.TestCase):
@patch('fdroidserver.install.download_apk') @patch('fdroidserver.install.download_apk')
@patch('fdroidserver.install.download_fdroid_apk') @patch('fdroidserver.install.download_fdroid_apk')
@patch('fdroidserver.install.download_fdroid_apk_from_github')
@patch('fdroidserver.install.download_fdroid_apk_from_ipns') @patch('fdroidserver.install.download_fdroid_apk_from_ipns')
@patch('fdroidserver.install.download_fdroid_apk_from_maven') @patch('fdroidserver.install.download_fdroid_apk_from_maven')
def test_install_fdroid_apk_privacy_mode_true( def test_install_fdroid_apk_privacy_mode_true(
self, maven, ipns, download_fdroid_apk, download_apk self, maven, ipns, github, download_fdroid_apk, download_apk
): ):
download_apk.side_effect = self._download_raise download_apk.side_effect = self._download_raise
download_fdroid_apk.side_effect = self._download_raise download_fdroid_apk.side_effect = self._download_raise
github.side_effect = self._download_raise
ipns.side_effect = self._download_raise ipns.side_effect = self._download_raise
maven.side_effect = self._download_raise maven.side_effect = self._download_raise
fdroidserver.common.config = {'jarsigner': 'fakepath'} fdroidserver.common.config = {'jarsigner': 'fakepath'}
install.install_fdroid_apk(privacy_mode=True) install.install_fdroid_apk(privacy_mode=True)
download_apk.assert_not_called() download_apk.assert_not_called()
download_fdroid_apk.assert_not_called() download_fdroid_apk.assert_not_called()
github.assert_called_once()
ipns.assert_called_once() ipns.assert_called_once()
maven.assert_called_once() maven.assert_called_once()
@patch('fdroidserver.install.download_apk') @patch('fdroidserver.install.download_apk')
@patch('fdroidserver.install.download_fdroid_apk') @patch('fdroidserver.install.download_fdroid_apk')
@patch('fdroidserver.install.download_fdroid_apk_from_github')
@patch('fdroidserver.install.download_fdroid_apk_from_ipns') @patch('fdroidserver.install.download_fdroid_apk_from_ipns')
@patch('fdroidserver.install.download_fdroid_apk_from_maven') @patch('fdroidserver.install.download_fdroid_apk_from_maven')
def test_install_fdroid_apk_privacy_mode_false( def test_install_fdroid_apk_privacy_mode_false(
self, maven, ipns, download_fdroid_apk, download_apk self, maven, ipns, github, download_fdroid_apk, download_apk
): ):
download_apk.side_effect = self._download_raise download_apk.side_effect = self._download_raise
download_fdroid_apk.side_effect = self._download_raise download_fdroid_apk.side_effect = self._download_raise
github.side_effect = self._download_raise
ipns.side_effect = self._download_raise ipns.side_effect = self._download_raise
maven.side_effect = self._download_raise maven.side_effect = self._download_raise
fdroidserver.common.config = {'jarsigner': 'fakepath'} fdroidserver.common.config = {'jarsigner': 'fakepath'}
install.install_fdroid_apk(privacy_mode=False) install.install_fdroid_apk(privacy_mode=False)
download_apk.assert_called_once() download_apk.assert_called_once()
download_fdroid_apk.assert_called_once() download_fdroid_apk.assert_called_once()
github.assert_called_once()
ipns.assert_called_once() ipns.assert_called_once()
maven.assert_called_once() maven.assert_called_once()
@patch('fdroidserver.install.download_apk') @patch('fdroidserver.install.download_apk')
@patch('fdroidserver.install.download_fdroid_apk') @patch('fdroidserver.install.download_fdroid_apk')
@patch('fdroidserver.install.download_fdroid_apk_from_github')
@patch('fdroidserver.install.download_fdroid_apk_from_ipns') @patch('fdroidserver.install.download_fdroid_apk_from_ipns')
@patch('fdroidserver.install.download_fdroid_apk_from_maven') @patch('fdroidserver.install.download_fdroid_apk_from_maven')
@patch('locale.getlocale', lambda: ('zh_CN', 'UTF-8')) @patch('locale.getlocale', lambda: ('zh_CN', 'UTF-8'))
def test_install_fdroid_apk_privacy_mode_locale_auto( def test_install_fdroid_apk_privacy_mode_locale_auto(
self, maven, ipns, download_fdroid_apk, download_apk self, maven, ipns, github, download_fdroid_apk, download_apk
): ):
download_apk.side_effect = self._download_raise download_apk.side_effect = self._download_raise
download_fdroid_apk.side_effect = self._download_raise download_fdroid_apk.side_effect = self._download_raise
github.side_effect = self._download_raise
ipns.side_effect = self._download_raise ipns.side_effect = self._download_raise
maven.side_effect = self._download_raise maven.side_effect = self._download_raise
fdroidserver.common.config = {'jarsigner': 'fakepath'} fdroidserver.common.config = {'jarsigner': 'fakepath'}
install.install_fdroid_apk(privacy_mode=None) install.install_fdroid_apk(privacy_mode=None)
download_apk.assert_not_called() download_apk.assert_not_called()
download_fdroid_apk.assert_not_called() download_fdroid_apk.assert_not_called()
github.assert_called_once()
ipns.assert_called_once() ipns.assert_called_once()
maven.assert_called_once() maven.assert_called_once()
@ -249,6 +258,11 @@ class InstallTest(unittest.TestCase):
f = install.download_fdroid_apk_from_ipns() f = install.download_fdroid_apk_from_ipns()
self.assertTrue(Path(f).exists()) self.assertTrue(Path(f).exists())
@unittest.skipUnless(os.getenv('test_download_fdroid_apk'), 'requires net access')
def test_download_fdroid_apk_from_github(self):
f = install.download_fdroid_apk_from_github()
self.assertTrue(Path(f).exists())
if __name__ == "__main__": if __name__ == "__main__":
os.chdir(os.path.dirname(__file__)) os.chdir(os.path.dirname(__file__))