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:
"""
Warpper for some select calls to GitHub Json/REST API.
"""Wrapper 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
general API wrapper. Instead it's purpose is to return pre-filtered and
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):
@ -41,9 +44,10 @@ class GithubApi:
def _req(self, url, data=None):
h = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {self._api_token}",
"X-GitHub-Api-Version": "2022-11-28",
}
if self._api_token:
h["Authorization"] = f"Bearer {self._api_token}"
return urllib.request.Request(
url,
headers=h,
@ -65,6 +69,17 @@ class GithubApi:
released_tags = self.list_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):
"""
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 . import _
from . import common, index, net
from . import common, github, index, net
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])
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):
"""Download the F-Droid APK from an IPNS repo."""
cid = 'k51qzi5uqu5dl4hbcksbdmplanu9n4hivnqsupqe6vzve1pdbeh418ssptldd3'
@ -161,11 +172,13 @@ def install_fdroid_apk(privacy_mode=False):
download_methods = [
download_fdroid_apk_from_maven,
download_fdroid_apk_from_ipns,
download_fdroid_apk_from_github,
]
else:
download_methods = [
download_apk,
download_fdroid_apk_from_maven,
download_fdroid_apk_from_github,
download_fdroid_apk_from_ipns,
download_fdroid_apk,
]

View File

@ -177,56 +177,65 @@ class InstallTest(unittest.TestCase):
@patch('fdroidserver.install.download_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_maven')
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_fdroid_apk.side_effect = self._download_raise
github.side_effect = self._download_raise
ipns.side_effect = self._download_raise
maven.side_effect = self._download_raise
fdroidserver.common.config = {'jarsigner': 'fakepath'}
install.install_fdroid_apk(privacy_mode=True)
download_apk.assert_not_called()
download_fdroid_apk.assert_not_called()
github.assert_called_once()
ipns.assert_called_once()
maven.assert_called_once()
@patch('fdroidserver.install.download_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_maven')
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_fdroid_apk.side_effect = self._download_raise
github.side_effect = self._download_raise
ipns.side_effect = self._download_raise
maven.side_effect = self._download_raise
fdroidserver.common.config = {'jarsigner': 'fakepath'}
install.install_fdroid_apk(privacy_mode=False)
download_apk.assert_called_once()
download_fdroid_apk.assert_called_once()
github.assert_called_once()
ipns.assert_called_once()
maven.assert_called_once()
@patch('fdroidserver.install.download_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_maven')
@patch('locale.getlocale', lambda: ('zh_CN', 'UTF-8'))
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_fdroid_apk.side_effect = self._download_raise
github.side_effect = self._download_raise
ipns.side_effect = self._download_raise
maven.side_effect = self._download_raise
fdroidserver.common.config = {'jarsigner': 'fakepath'}
install.install_fdroid_apk(privacy_mode=None)
download_apk.assert_not_called()
download_fdroid_apk.assert_not_called()
github.assert_called_once()
ipns.assert_called_once()
maven.assert_called_once()
@ -249,6 +258,11 @@ class InstallTest(unittest.TestCase):
f = install.download_fdroid_apk_from_ipns()
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__":
os.chdir(os.path.dirname(__file__))