1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-19 21:30:10 +01:00

Move methods specific to import to it's module

This commit is contained in:
FestplattenSchnitzel 2022-08-06 10:21:12 +02:00 committed by Hans-Christoph Steiner
parent 7b7f863c65
commit 7c89e923f6
4 changed files with 145 additions and 142 deletions

View File

@ -45,8 +45,6 @@ import logging
import hashlib import hashlib
import socket import socket
import base64 import base64
import urllib.parse
import urllib.request
import yaml import yaml
import zipfile import zipfile
import tempfile import tempfile
@ -1944,121 +1942,6 @@ def get_gradle_subdir(build_dir, paths):
return return
def getrepofrompage(url):
"""Get the repo type and address from the given web page.
The page is scanned in a rather naive manner for 'git clone xxxx',
'hg clone xxxx', etc, and when one of these is found it's assumed
that's the information we want. Returns repotype, address, or
None, reason
"""
if not url.startswith('http'):
return (None, _('{url} does not start with "http"!'.format(url=url)))
req = urllib.request.urlopen(url) # nosec B310 non-http URLs are filtered out
if req.getcode() != 200:
return (None, 'Unable to get ' + url + ' - return code ' + str(req.getcode()))
page = req.read().decode(req.headers.get_content_charset())
# Works for BitBucket
m = re.search('data-fetch-url="(.*)"', page)
if m is not None:
repo = m.group(1)
if repo.endswith('.git'):
return ('git', repo)
return ('hg', repo)
# Works for BitBucket (obsolete)
index = page.find('hg clone')
if index != -1:
repotype = 'hg'
repo = page[index + 9:]
index = repo.find('<')
if index == -1:
return (None, _("Error while getting repo address"))
repo = repo[:index]
repo = repo.split('"')[0]
return (repotype, repo)
# Works for BitBucket (obsolete)
index = page.find('git clone')
if index != -1:
repotype = 'git'
repo = page[index + 10:]
index = repo.find('<')
if index == -1:
return (None, _("Error while getting repo address"))
repo = repo[:index]
repo = repo.split('"')[0]
return (repotype, repo)
return (None, _("No information found.") + page)
def get_app_from_url(url):
"""Guess basic app metadata from the URL.
The URL must include a network hostname, unless it is an lp:,
file:, or git/ssh URL. This throws ValueError on bad URLs to
match urlparse().
"""
parsed = urllib.parse.urlparse(url)
invalid_url = False
if not parsed.scheme or not parsed.path:
invalid_url = True
app = fdroidserver.metadata.App()
app.Repo = url
if url.startswith('git://') or url.startswith('git@'):
app.RepoType = 'git'
elif parsed.netloc == 'github.com':
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif parsed.netloc == 'gitlab.com' or parsed.netloc == 'framagit.org':
# git can be fussy with gitlab URLs unless they end in .git
if url.endswith('.git'):
url = url[:-4]
app.Repo = url + '.git'
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif parsed.netloc == 'notabug.org':
if url.endswith('.git'):
url = url[:-4]
app.Repo = url + '.git'
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif parsed.netloc == 'bitbucket.org':
if url.endswith('/'):
url = url[:-1]
app.SourceCode = url + '/src'
app.IssueTracker = url + '/issues'
# Figure out the repo type and adddress...
app.RepoType, app.Repo = getrepofrompage(url)
elif parsed.netloc == 'codeberg.org':
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif url.startswith('https://') and url.endswith('.git'):
app.RepoType = 'git'
if not parsed.netloc and parsed.scheme in ('git', 'http', 'https', 'ssh'):
invalid_url = True
if invalid_url:
raise ValueError(_('"{url}" is not a valid URL!'.format(url=url)))
if not app.RepoType:
raise FDroidException("Unable to determine vcs type. " + app.Repo)
return app
def parse_srclib_spec(spec): def parse_srclib_spec(spec):
if type(spec) != str: if type(spec) != str:
@ -4609,10 +4492,3 @@ NDKS = [
"url": "https://dl.google.com/android/repository/android-ndk-r25b-linux.zip" "url": "https://dl.google.com/android/repository/android-ndk-r25b-linux.zip"
} }
] ]
def handle_retree_error_on_windows(function, path, excinfo):
"""Python can't remove a readonly file on Windows so chmod first."""
if function in (os.unlink, os.rmdir, os.remove) and excinfo[0] == PermissionError:
os.chmod(path, stat.S_IWRITE)
function(path)

View File

@ -18,6 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import configparser import configparser
import os
import re
import stat
import urllib
import git import git
import json import json
import shutil import shutil
@ -42,6 +47,13 @@ config = None
options = None options = None
def handle_retree_error_on_windows(function, path, excinfo):
"""Python can't remove a readonly file on Windows so chmod first."""
if function in (os.unlink, os.rmdir, os.remove) and excinfo[0] == PermissionError:
os.chmod(path, stat.S_IWRITE)
function(path)
def clone_to_tmp_dir(app): def clone_to_tmp_dir(app):
tmp_dir = Path('tmp') tmp_dir = Path('tmp')
tmp_dir.mkdir(exist_ok=True) tmp_dir.mkdir(exist_ok=True)
@ -49,13 +61,128 @@ def clone_to_tmp_dir(app):
tmp_dir = tmp_dir / 'importer' tmp_dir = tmp_dir / 'importer'
if tmp_dir.exists(): if tmp_dir.exists():
shutil.rmtree(str(tmp_dir), onerror=common.handle_retree_error_on_windows) shutil.rmtree(str(tmp_dir), onerror=handle_retree_error_on_windows)
vcs = common.getvcs(app.RepoType, app.Repo, tmp_dir) vcs = common.getvcs(app.RepoType, app.Repo, tmp_dir)
vcs.gotorevision(options.rev) vcs.gotorevision(options.rev)
return tmp_dir return tmp_dir
def getrepofrompage(url):
"""Get the repo type and address from the given web page.
The page is scanned in a rather naive manner for 'git clone xxxx',
'hg clone xxxx', etc, and when one of these is found it's assumed
that's the information we want. Returns repotype, address, or
None, reason
"""
if not url.startswith('http'):
return (None, _('{url} does not start with "http"!'.format(url=url)))
req = urllib.request.urlopen(url) # nosec B310 non-http URLs are filtered out
if req.getcode() != 200:
return (None, 'Unable to get ' + url + ' - return code ' + str(req.getcode()))
page = req.read().decode(req.headers.get_content_charset())
# Works for BitBucket
m = re.search('data-fetch-url="(.*)"', page)
if m is not None:
repo = m.group(1)
if repo.endswith('.git'):
return ('git', repo)
return ('hg', repo)
# Works for BitBucket (obsolete)
index = page.find('hg clone')
if index != -1:
repotype = 'hg'
repo = page[index + 9:]
index = repo.find('<')
if index == -1:
return (None, _("Error while getting repo address"))
repo = repo[:index]
repo = repo.split('"')[0]
return (repotype, repo)
# Works for BitBucket (obsolete)
index = page.find('git clone')
if index != -1:
repotype = 'git'
repo = page[index + 10:]
index = repo.find('<')
if index == -1:
return (None, _("Error while getting repo address"))
repo = repo[:index]
repo = repo.split('"')[0]
return (repotype, repo)
return (None, _("No information found.") + page)
def get_app_from_url(url):
"""Guess basic app metadata from the URL.
The URL must include a network hostname, unless it is an lp:,
file:, or git/ssh URL. This throws ValueError on bad URLs to
match urlparse().
"""
parsed = urllib.parse.urlparse(url)
invalid_url = False
if not parsed.scheme or not parsed.path:
invalid_url = True
app = metadata.App()
app.Repo = url
if url.startswith('git://') or url.startswith('git@'):
app.RepoType = 'git'
elif parsed.netloc == 'github.com':
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif parsed.netloc == 'gitlab.com' or parsed.netloc == 'framagit.org':
# git can be fussy with gitlab URLs unless they end in .git
if url.endswith('.git'):
url = url[:-4]
app.Repo = url + '.git'
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif parsed.netloc == 'notabug.org':
if url.endswith('.git'):
url = url[:-4]
app.Repo = url + '.git'
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif parsed.netloc == 'bitbucket.org':
if url.endswith('/'):
url = url[:-1]
app.SourceCode = url + '/src'
app.IssueTracker = url + '/issues'
# Figure out the repo type and adddress...
app.RepoType, app.Repo = getrepofrompage(url)
elif parsed.netloc == 'codeberg.org':
app.RepoType = 'git'
app.SourceCode = url
app.IssueTracker = url + '/issues'
elif url.startswith('https://') and url.endswith('.git'):
app.RepoType = 'git'
if not parsed.netloc and parsed.scheme in ('git', 'http', 'https', 'ssh'):
invalid_url = True
if invalid_url:
raise ValueError(_('"{url}" is not a valid URL!'.format(url=url)))
if not app.RepoType:
raise FDroidException("Unable to determine vcs type. " + app.Repo)
return app
def check_for_kivy_buildozer(tmp_importer_dir, app, build): def check_for_kivy_buildozer(tmp_importer_dir, app, build):
versionCode = None versionCode = None
buildozer_spec = tmp_importer_dir / 'buildozer.spec' buildozer_spec = tmp_importer_dir / 'buildozer.spec'
@ -148,7 +275,7 @@ def main():
break break
write_local_file = True write_local_file = True
elif options.url: elif options.url:
app = common.get_app_from_url(options.url) app = get_app_from_url(options.url)
tmp_importer_dir = clone_to_tmp_dir(app) tmp_importer_dir = clone_to_tmp_dir(app)
# TODO: Python3.6: Should accept path-like # TODO: Python3.6: Should accept path-like
git_repo = git.Repo(str(tmp_importer_dir)) git_repo = git.Repo(str(tmp_importer_dir))

View File

@ -1419,19 +1419,6 @@ class CommonTest(unittest.TestCase):
with self.assertRaises(MetaDataException): with self.assertRaises(MetaDataException):
self.assertEqual(fdroidserver.common.parse_srclib_spec('@multi@at-signs@')) self.assertEqual(fdroidserver.common.parse_srclib_spec('@multi@at-signs@'))
def test_bad_urls(self):
for url in (
'asdf',
'file://thing.git',
'https:///github.com/my/project',
'git:///so/many/slashes',
'ssh:/notabug.org/missing/a/slash',
'git:notabug.org/missing/some/slashes',
'https//github.com/bar/baz',
):
with self.assertRaises(ValueError):
fdroidserver.common.get_app_from_url(url)
def test_remove_signing_keys(self): def test_remove_signing_keys(self):
testdir = tempfile.mkdtemp( testdir = tempfile.mkdtemp(
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir

View File

@ -51,7 +51,7 @@ class ImportTest(unittest.TestCase):
print('Skipping ImportTest!') print('Skipping ImportTest!')
return return
app = fdroidserver.common.get_app_from_url(url) app = fdroidserver.import_subcommand.get_app_from_url(url)
fdroidserver.import_subcommand.clone_to_tmp_dir(app) fdroidserver.import_subcommand.clone_to_tmp_dir(app)
self.assertEqual(app.RepoType, 'git') self.assertEqual(app.RepoType, 'git')
self.assertEqual(app.Repo, 'https://gitlab.com/fdroid/ci-test-app.git') self.assertEqual(app.Repo, 'https://gitlab.com/fdroid/ci-test-app.git')
@ -88,13 +88,13 @@ class ImportTest(unittest.TestCase):
# TODO: Python3.6: Accepts a path-like object. # TODO: Python3.6: Accepts a path-like object.
shutil.rmtree( shutil.rmtree(
str(tmp_importer), str(tmp_importer),
onerror=fdroidserver.common.handle_retree_error_on_windows, onerror=fdroidserver.import_subcommand.handle_retree_error_on_windows,
) )
shutil.copytree( shutil.copytree(
str(self.basedir / 'source-files' / appid), str(tmp_importer) str(self.basedir / 'source-files' / appid), str(tmp_importer)
) )
app = fdroidserver.common.get_app_from_url(url) app = fdroidserver.import_subcommand.get_app_from_url(url)
with mock.patch( with mock.patch(
'fdroidserver.common.getvcs', 'fdroidserver.common.getvcs',
lambda a, b, c: fdroidserver.common.vcs(url, testdir), lambda a, b, c: fdroidserver.common.vcs(url, testdir),
@ -119,6 +119,19 @@ class ImportTest(unittest.TestCase):
self.assertEqual(vc, versionCode) self.assertEqual(vc, versionCode)
self.assertEqual(appid, package) self.assertEqual(appid, package)
def test_bad_urls(self):
for url in (
'asdf',
'file://thing.git',
'https:///github.com/my/project',
'git:///so/many/slashes',
'ssh:/notabug.org/missing/a/slash',
'git:notabug.org/missing/some/slashes',
'https//github.com/bar/baz',
):
with self.assertRaises(ValueError):
fdroidserver.import_subcommand.get_app_from_url(url)
if __name__ == "__main__": if __name__ == "__main__":
parser = optparse.OptionParser() parser = optparse.OptionParser()