mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-20 13:50:12 +01:00
run black to reformat code that does not have WIP merge requests
This commit is contained in:
parent
2e6cad57aa
commit
307cf8958c
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
@ -8,7 +7,9 @@ import sys
|
|||||||
# support running straight from git and standard installs
|
# support running straight from git and standard installs
|
||||||
rootpaths = [
|
rootpaths = [
|
||||||
os.path.realpath(os.path.join(os.path.dirname(__file__), '..')),
|
os.path.realpath(os.path.join(os.path.dirname(__file__), '..')),
|
||||||
os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'share')),
|
os.path.realpath(
|
||||||
|
os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'share')
|
||||||
|
),
|
||||||
os.path.join(sys.prefix, 'share'),
|
os.path.join(sys.prefix, 'share'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -59,7 +59,9 @@ def main():
|
|||||||
signed = []
|
signed = []
|
||||||
for output_dir in repodirs:
|
for output_dir in repodirs:
|
||||||
if not os.path.isdir(output_dir):
|
if not os.path.isdir(output_dir):
|
||||||
raise FDroidException(_("Missing output directory") + " '" + output_dir + "'")
|
raise FDroidException(
|
||||||
|
_("Missing output directory") + " '" + output_dir + "'"
|
||||||
|
)
|
||||||
|
|
||||||
# Process any apks that are waiting to be signed...
|
# Process any apks that are waiting to be signed...
|
||||||
for f in sorted(glob.glob(os.path.join(output_dir, '*.*'))):
|
for f in sorted(glob.glob(os.path.join(output_dir, '*.*'))):
|
||||||
|
@ -38,6 +38,7 @@ options = None
|
|||||||
def disable_in_config(key, value):
|
def disable_in_config(key, value):
|
||||||
"""Write a key/value to the local config.yml, then comment it out."""
|
"""Write a key/value to the local config.yml, then comment it out."""
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
with open('config.yml') as f:
|
with open('config.yml') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
pattern = r'\n[\s#]*' + key + r':.*'
|
pattern = r'\n[\s#]*' + key + r':.*'
|
||||||
@ -54,16 +55,33 @@ def main():
|
|||||||
# Parse command line...
|
# Parse command line...
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
common.setup_global_opts(parser)
|
common.setup_global_opts(parser)
|
||||||
parser.add_argument("-d", "--distinguished-name", default=None,
|
parser.add_argument(
|
||||||
help=_("X.509 'Distinguished Name' used when generating keys"))
|
"-d",
|
||||||
parser.add_argument("--keystore", default=None,
|
"--distinguished-name",
|
||||||
help=_("Path to the keystore for the repo signing key"))
|
default=None,
|
||||||
parser.add_argument("--repo-keyalias", default=None,
|
help=_("X.509 'Distinguished Name' used when generating keys"),
|
||||||
help=_("Alias of the repo signing key in the keystore"))
|
)
|
||||||
parser.add_argument("--android-home", default=None,
|
parser.add_argument(
|
||||||
help=_("Path to the Android SDK (sometimes set in ANDROID_HOME)"))
|
"--keystore",
|
||||||
parser.add_argument("--no-prompt", action="store_true", default=False,
|
default=None,
|
||||||
help=_("Do not prompt for Android SDK path, just fail"))
|
help=_("Path to the keystore for the repo signing key"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--repo-keyalias",
|
||||||
|
default=None,
|
||||||
|
help=_("Alias of the repo signing key in the keystore"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--android-home",
|
||||||
|
default=None,
|
||||||
|
help=_("Path to the Android SDK (sometimes set in ANDROID_HOME)"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-prompt",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help=_("Do not prompt for Android SDK path, just fail"),
|
||||||
|
)
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
fdroiddir = os.getcwd()
|
fdroiddir = os.getcwd()
|
||||||
@ -81,8 +99,9 @@ def main():
|
|||||||
# and if the user leaves it blank, ignore and move on.
|
# and if the user leaves it blank, ignore and move on.
|
||||||
default_sdk_path = ''
|
default_sdk_path = ''
|
||||||
if sys.platform == 'win32' or sys.platform == 'cygwin':
|
if sys.platform == 'win32' or sys.platform == 'cygwin':
|
||||||
p = os.path.join(os.getenv('USERPROFILE'),
|
p = os.path.join(
|
||||||
'AppData', 'Local', 'Android', 'android-sdk')
|
os.getenv('USERPROFILE'), 'AppData', 'Local', 'Android', 'android-sdk'
|
||||||
|
)
|
||||||
elif sys.platform == 'darwin':
|
elif sys.platform == 'darwin':
|
||||||
# on OSX, Homebrew is common and has an easy path to detect
|
# on OSX, Homebrew is common and has an easy path to detect
|
||||||
p = '/usr/local/opt/android-sdk'
|
p = '/usr/local/opt/android-sdk'
|
||||||
@ -96,10 +115,13 @@ def main():
|
|||||||
test_config['sdk_path'] = default_sdk_path
|
test_config['sdk_path'] = default_sdk_path
|
||||||
|
|
||||||
if not common.test_sdk_exists(test_config):
|
if not common.test_sdk_exists(test_config):
|
||||||
del(test_config['sdk_path'])
|
del (test_config['sdk_path'])
|
||||||
while not options.no_prompt:
|
while not options.no_prompt:
|
||||||
try:
|
try:
|
||||||
s = input(_('Enter the path to the Android SDK (%s) here:\n> ') % default_sdk_path)
|
s = input(
|
||||||
|
_('Enter the path to the Android SDK (%s) here:\n> ')
|
||||||
|
% default_sdk_path
|
||||||
|
)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('')
|
print('')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -112,8 +134,9 @@ def main():
|
|||||||
default_sdk_path = ''
|
default_sdk_path = ''
|
||||||
|
|
||||||
if test_config.get('sdk_path') and not common.test_sdk_exists(test_config):
|
if test_config.get('sdk_path') and not common.test_sdk_exists(test_config):
|
||||||
raise FDroidException(_("Android SDK not found at {path}!")
|
raise FDroidException(
|
||||||
.format(path=test_config['sdk_path']))
|
_("Android SDK not found at {path}!").format(path=test_config['sdk_path'])
|
||||||
|
)
|
||||||
|
|
||||||
if not os.path.exists('config.yml') and not os.path.exists('config.py'):
|
if not os.path.exists('config.yml') and not os.path.exists('config.py'):
|
||||||
# 'metadata' and 'tmp' are created in fdroid
|
# 'metadata' and 'tmp' are created in fdroid
|
||||||
@ -124,12 +147,14 @@ def main():
|
|||||||
shutil.copyfile(example_config_yml, 'config.yml')
|
shutil.copyfile(example_config_yml, 'config.yml')
|
||||||
else:
|
else:
|
||||||
from pkg_resources import get_distribution
|
from pkg_resources import get_distribution
|
||||||
|
|
||||||
versionstr = get_distribution('fdroidserver').version
|
versionstr = get_distribution('fdroidserver').version
|
||||||
if not versionstr:
|
if not versionstr:
|
||||||
versionstr = 'master'
|
versionstr = 'master'
|
||||||
with open('config.yml', 'w') as fp:
|
with open('config.yml', 'w') as fp:
|
||||||
fp.write('# see https://gitlab.com/fdroid/fdroidserver/blob/'
|
fp.write('# see https://gitlab.com/fdroid/fdroidserver/blob/')
|
||||||
+ versionstr + '/examples/config.yml\n')
|
fp.write(versionstr)
|
||||||
|
fp.write('/examples/config.yml\n')
|
||||||
os.chmod('config.yml', 0o0600)
|
os.chmod('config.yml', 0o0600)
|
||||||
# If android_home is None, test_config['sdk_path'] will be used and
|
# If android_home is None, test_config['sdk_path'] will be used and
|
||||||
# "$ANDROID_HOME" may be used if the env var is set up correctly.
|
# "$ANDROID_HOME" may be used if the env var is set up correctly.
|
||||||
@ -138,7 +163,9 @@ def main():
|
|||||||
if 'sdk_path' in test_config:
|
if 'sdk_path' in test_config:
|
||||||
common.write_to_config(test_config, 'sdk_path', options.android_home)
|
common.write_to_config(test_config, 'sdk_path', options.android_home)
|
||||||
else:
|
else:
|
||||||
logging.warning('Looks like this is already an F-Droid repo, cowardly refusing to overwrite it...')
|
logging.warning(
|
||||||
|
'Looks like this is already an F-Droid repo, cowardly refusing to overwrite it...'
|
||||||
|
)
|
||||||
logging.info('Try running `fdroid init` in an empty directory.')
|
logging.info('Try running `fdroid init` in an empty directory.')
|
||||||
raise FDroidException('Repository already exists.')
|
raise FDroidException('Repository already exists.')
|
||||||
|
|
||||||
@ -162,8 +189,9 @@ def main():
|
|||||||
else:
|
else:
|
||||||
keystore = os.path.abspath(options.keystore)
|
keystore = os.path.abspath(options.keystore)
|
||||||
if not os.path.exists(keystore):
|
if not os.path.exists(keystore):
|
||||||
logging.info('"' + keystore
|
logging.info(
|
||||||
+ '" does not exist, creating a new keystore there.')
|
'"' + keystore + '" does not exist, creating a new keystore there.'
|
||||||
|
)
|
||||||
common.write_to_config(test_config, 'keystore', keystore)
|
common.write_to_config(test_config, 'keystore', keystore)
|
||||||
repo_keyalias = None
|
repo_keyalias = None
|
||||||
keydname = None
|
keydname = None
|
||||||
@ -174,12 +202,19 @@ def main():
|
|||||||
keydname = options.distinguished_name
|
keydname = options.distinguished_name
|
||||||
common.write_to_config(test_config, 'keydname', keydname)
|
common.write_to_config(test_config, 'keydname', keydname)
|
||||||
if keystore == 'NONE': # we're using a smartcard
|
if keystore == 'NONE': # we're using a smartcard
|
||||||
common.write_to_config(test_config, 'repo_keyalias', '1') # seems to be the default
|
common.write_to_config(
|
||||||
|
test_config, 'repo_keyalias', '1'
|
||||||
|
) # seems to be the default
|
||||||
disable_in_config('keypass', 'never used with smartcard')
|
disable_in_config('keypass', 'never used with smartcard')
|
||||||
common.write_to_config(test_config, 'smartcardoptions',
|
common.write_to_config(
|
||||||
('-storetype PKCS11 '
|
test_config,
|
||||||
+ '-providerClass sun.security.pkcs11.SunPKCS11 '
|
'smartcardoptions',
|
||||||
+ '-providerArg opensc-fdroid.cfg'))
|
(
|
||||||
|
'-storetype PKCS11 '
|
||||||
|
+ '-providerClass sun.security.pkcs11.SunPKCS11 '
|
||||||
|
+ '-providerArg opensc-fdroid.cfg'
|
||||||
|
),
|
||||||
|
)
|
||||||
# find opensc-pkcs11.so
|
# find opensc-pkcs11.so
|
||||||
if not os.path.exists('opensc-fdroid.cfg'):
|
if not os.path.exists('opensc-fdroid.cfg'):
|
||||||
if os.path.exists('/usr/lib/opensc-pkcs11.so'):
|
if os.path.exists('/usr/lib/opensc-pkcs11.so'):
|
||||||
@ -187,29 +222,43 @@ def main():
|
|||||||
elif os.path.exists('/usr/lib64/opensc-pkcs11.so'):
|
elif os.path.exists('/usr/lib64/opensc-pkcs11.so'):
|
||||||
opensc_so = '/usr/lib64/opensc-pkcs11.so'
|
opensc_so = '/usr/lib64/opensc-pkcs11.so'
|
||||||
else:
|
else:
|
||||||
files = glob.glob('/usr/lib/' + os.uname()[4] + '-*-gnu/opensc-pkcs11.so')
|
files = glob.glob(
|
||||||
|
'/usr/lib/' + os.uname()[4] + '-*-gnu/opensc-pkcs11.so'
|
||||||
|
)
|
||||||
if len(files) > 0:
|
if len(files) > 0:
|
||||||
opensc_so = files[0]
|
opensc_so = files[0]
|
||||||
else:
|
else:
|
||||||
opensc_so = '/usr/lib/opensc-pkcs11.so'
|
opensc_so = '/usr/lib/opensc-pkcs11.so'
|
||||||
logging.warning('No OpenSC PKCS#11 module found, '
|
logging.warning(
|
||||||
+ 'install OpenSC then edit "opensc-fdroid.cfg"!')
|
'No OpenSC PKCS#11 module found, '
|
||||||
|
+ 'install OpenSC then edit "opensc-fdroid.cfg"!'
|
||||||
|
)
|
||||||
with open('opensc-fdroid.cfg', 'w') as f:
|
with open('opensc-fdroid.cfg', 'w') as f:
|
||||||
f.write('name = OpenSC\nlibrary = ')
|
f.write('name = OpenSC\nlibrary = ')
|
||||||
f.write(opensc_so)
|
f.write(opensc_so)
|
||||||
f.write('\n')
|
f.write('\n')
|
||||||
logging.info("Repo setup using a smartcard HSM. Please edit keystorepass and repo_keyalias in config.yml.")
|
logging.info(
|
||||||
logging.info("If you want to generate a new repo signing key in the HSM you can do that with 'fdroid update "
|
"Repo setup using a smartcard HSM. Please edit keystorepass and repo_keyalias in config.yml."
|
||||||
"--create-key'.")
|
)
|
||||||
|
logging.info(
|
||||||
|
"If you want to generate a new repo signing key in the HSM you can do that with 'fdroid update "
|
||||||
|
"--create-key'."
|
||||||
|
)
|
||||||
elif os.path.exists(keystore):
|
elif os.path.exists(keystore):
|
||||||
to_set = ['keystorepass', 'keypass', 'repo_keyalias', 'keydname']
|
to_set = ['keystorepass', 'keypass', 'repo_keyalias', 'keydname']
|
||||||
if repo_keyalias:
|
if repo_keyalias:
|
||||||
to_set.remove('repo_keyalias')
|
to_set.remove('repo_keyalias')
|
||||||
if keydname:
|
if keydname:
|
||||||
to_set.remove('keydname')
|
to_set.remove('keydname')
|
||||||
logging.warning('\n' + _('Using existing keystore "{path}"').format(path=keystore)
|
logging.warning(
|
||||||
+ '\n' + _('Now set these in config.yml:') + ' '
|
'\n'
|
||||||
+ ', '.join(to_set) + '\n')
|
+ _('Using existing keystore "{path}"').format(path=keystore)
|
||||||
|
+ '\n'
|
||||||
|
+ _('Now set these in config.yml:')
|
||||||
|
+ ' '
|
||||||
|
+ ', '.join(to_set)
|
||||||
|
+ '\n'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
password = common.genpassword()
|
password = common.genpassword()
|
||||||
c = dict(test_config)
|
c = dict(test_config)
|
||||||
@ -229,11 +278,17 @@ def main():
|
|||||||
msg += '\n ' + _('Keystore for signing key:\t') + keystore
|
msg += '\n ' + _('Keystore for signing key:\t') + keystore
|
||||||
if repo_keyalias is not None:
|
if repo_keyalias is not None:
|
||||||
msg += '\n Alias for key in store:\t' + repo_keyalias
|
msg += '\n Alias for key in store:\t' + repo_keyalias
|
||||||
msg += '\n\n' + '''To complete the setup, add your APKs to "%s"
|
msg += '\n\n'
|
||||||
|
msg += (
|
||||||
|
_(
|
||||||
|
'''To complete the setup, add your APKs to "%s"
|
||||||
then run "fdroid update -c; fdroid update". You might also want to edit
|
then run "fdroid update -c; fdroid update". You might also want to edit
|
||||||
"config.yml" to set the URL, repo name, and more. You should also set up
|
"config.yml" to set the URL, repo name, and more. You should also set up
|
||||||
a signing key (a temporary one might have been automatically generated).
|
a signing key (a temporary one might have been automatically generated).
|
||||||
|
|
||||||
For more info: https://f-droid.org/docs/Setup_an_F-Droid_App_Repo
|
For more info: https://f-droid.org/docs/Setup_an_F-Droid_App_Repo
|
||||||
and https://f-droid.org/docs/Signing_Process''' % os.path.join(fdroiddir, 'repo')
|
and https://f-droid.org/docs/Signing_Process'''
|
||||||
|
)
|
||||||
|
% os.path.join(fdroiddir, 'repo')
|
||||||
|
)
|
||||||
logging.info(msg)
|
logging.info(msg)
|
||||||
|
@ -33,8 +33,15 @@ def _run_wget(path, urls):
|
|||||||
with open(urls_file, 'w') as fp:
|
with open(urls_file, 'w') as fp:
|
||||||
for url in urls:
|
for url in urls:
|
||||||
fp.write(url.split('?')[0] + '\n') # wget puts query string in the filename
|
fp.write(url.split('?')[0] + '\n') # wget puts query string in the filename
|
||||||
subprocess.call(['wget', verbose, '--continue', '--user-agent="fdroid mirror"',
|
subprocess.call(
|
||||||
'--input-file=' + urls_file])
|
[
|
||||||
|
'wget',
|
||||||
|
verbose,
|
||||||
|
'--continue',
|
||||||
|
'--user-agent="fdroid mirror"',
|
||||||
|
'--input-file=' + urls_file,
|
||||||
|
]
|
||||||
|
)
|
||||||
os.remove(urls_file)
|
os.remove(urls_file)
|
||||||
|
|
||||||
|
|
||||||
@ -43,21 +50,47 @@ def main():
|
|||||||
|
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
common.setup_global_opts(parser)
|
common.setup_global_opts(parser)
|
||||||
parser.add_argument("url", nargs='?',
|
parser.add_argument(
|
||||||
help=_('Base URL to mirror, can include the index signing key '
|
"url",
|
||||||
+ 'using the query string: ?fingerprint='))
|
nargs='?',
|
||||||
parser.add_argument("--all", action='store_true', default=False,
|
help=_(
|
||||||
help=_("Mirror the full repo and archive, all file types."))
|
'Base URL to mirror, can include the index signing key '
|
||||||
parser.add_argument("--archive", action='store_true', default=False,
|
+ 'using the query string: ?fingerprint='
|
||||||
help=_("Also mirror the full archive section"))
|
),
|
||||||
parser.add_argument("--build-logs", action='store_true', default=False,
|
)
|
||||||
help=_("Include the build logs in the mirror"))
|
parser.add_argument(
|
||||||
parser.add_argument("--pgp-signatures", action='store_true', default=False,
|
"--all",
|
||||||
help=_("Include the PGP signature .asc files in the mirror"))
|
action='store_true',
|
||||||
parser.add_argument("--src-tarballs", action='store_true', default=False,
|
default=False,
|
||||||
help=_("Include the source tarballs in the mirror"))
|
help=_("Mirror the full repo and archive, all file types."),
|
||||||
parser.add_argument("--output-dir", default=None,
|
)
|
||||||
help=_("The directory to write the mirror to"))
|
parser.add_argument(
|
||||||
|
"--archive",
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_("Also mirror the full archive section"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--build-logs",
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_("Include the build logs in the mirror"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--pgp-signatures",
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_("Include the PGP signature .asc files in the mirror"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--src-tarballs",
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_("Include the source tarballs in the mirror"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output-dir", default=None, help=_("The directory to write the mirror to")
|
||||||
|
)
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
if options.all:
|
if options.all:
|
||||||
@ -77,24 +110,31 @@ def main():
|
|||||||
def _append_to_url_path(*args):
|
def _append_to_url_path(*args):
|
||||||
"""Append the list of path components to URL, keeping the rest the same."""
|
"""Append the list of path components to URL, keeping the rest the same."""
|
||||||
newpath = posixpath.join(path, *args)
|
newpath = posixpath.join(path, *args)
|
||||||
return urllib.parse.urlunparse((scheme, hostname, newpath, params, query, fragment))
|
return urllib.parse.urlunparse(
|
||||||
|
(scheme, hostname, newpath, params, query, fragment)
|
||||||
|
)
|
||||||
|
|
||||||
if fingerprint:
|
if fingerprint:
|
||||||
config = common.read_config(options)
|
config = common.read_config(options)
|
||||||
if not ('jarsigner' in config or 'apksigner' in config):
|
if not ('jarsigner' in config or 'apksigner' in config):
|
||||||
logging.error(_('Java JDK not found! Install in standard location or set java_paths!'))
|
logging.error(
|
||||||
|
_('Java JDK not found! Install in standard location or set java_paths!')
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def _get_index(section, etag=None):
|
def _get_index(section, etag=None):
|
||||||
url = _append_to_url_path(section)
|
url = _append_to_url_path(section)
|
||||||
data, etag = index.download_repo_index(url, etag=etag)
|
data, etag = index.download_repo_index(url, etag=etag)
|
||||||
return data, etag, _append_to_url_path(section, 'index-v1.jar')
|
return data, etag, _append_to_url_path(section, 'index-v1.jar')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def _get_index(section, etag=None):
|
def _get_index(section, etag=None):
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import zipfile
|
import zipfile
|
||||||
from . import net
|
from . import net
|
||||||
|
|
||||||
url = _append_to_url_path(section, 'index-v1.jar')
|
url = _append_to_url_path(section, 'index-v1.jar')
|
||||||
content, etag = net.http_get(url)
|
content, etag = net.http_get(url)
|
||||||
with zipfile.ZipFile(io.BytesIO(content)) as zip:
|
with zipfile.ZipFile(io.BytesIO(content)) as zip:
|
||||||
@ -107,21 +147,30 @@ def main():
|
|||||||
ip = ipaddress.ip_address(hostname)
|
ip = ipaddress.ip_address(hostname)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
if hostname == 'f-droid.org' \
|
if hostname == 'f-droid.org' or (
|
||||||
or (ip is not None and hostname in socket.gethostbyname_ex('f-droid.org')[2]):
|
ip is not None and hostname in socket.gethostbyname_ex('f-droid.org')[2]
|
||||||
print(_('ERROR: this command should never be used to mirror f-droid.org!\n'
|
):
|
||||||
'A full mirror of f-droid.org requires more than 200GB.'))
|
print(
|
||||||
|
_(
|
||||||
|
'ERROR: this command should never be used to mirror f-droid.org!\n'
|
||||||
|
'A full mirror of f-droid.org requires more than 200GB.'
|
||||||
|
)
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
path = path.rstrip('/')
|
path = path.rstrip('/')
|
||||||
if path.endswith('repo') or path.endswith('archive'):
|
if path.endswith('repo') or path.endswith('archive'):
|
||||||
logging.warning(_('Do not include "{path}" in URL!')
|
logging.warning(
|
||||||
.format(path=path.split('/')[-1]))
|
_('Do not include "{path}" in URL!').format(path=path.split('/')[-1])
|
||||||
|
)
|
||||||
elif not path.endswith('fdroid'):
|
elif not path.endswith('fdroid'):
|
||||||
logging.warning(_('{url} does not end with "fdroid", check the URL path!')
|
logging.warning(
|
||||||
.format(url=options.url))
|
_('{url} does not end with "fdroid", check the URL path!').format(
|
||||||
|
url=options.url
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
icondirs = ['icons', ]
|
icondirs = ['icons']
|
||||||
for density in update.screen_densities:
|
for density in update.screen_densities:
|
||||||
icondirs.append('icons-' + density)
|
icondirs.append('icons-' + density)
|
||||||
|
|
||||||
@ -134,7 +183,7 @@ def main():
|
|||||||
if options.archive:
|
if options.archive:
|
||||||
sections = ('repo', 'archive')
|
sections = ('repo', 'archive')
|
||||||
else:
|
else:
|
||||||
sections = ('repo', )
|
sections = ('repo',)
|
||||||
|
|
||||||
for section in sections:
|
for section in sections:
|
||||||
sectiondir = os.path.join(basedir, section)
|
sectiondir = os.path.join(basedir, section)
|
||||||
@ -152,23 +201,29 @@ def main():
|
|||||||
for packageName, packageList in data['packages'].items():
|
for packageName, packageList in data['packages'].items():
|
||||||
for package in packageList:
|
for package in packageList:
|
||||||
to_fetch = []
|
to_fetch = []
|
||||||
keys = ['apkName', ]
|
keys = ['apkName']
|
||||||
if options.src_tarballs:
|
if options.src_tarballs:
|
||||||
keys.append('srcname')
|
keys.append('srcname')
|
||||||
for k in keys:
|
for k in keys:
|
||||||
if k in package:
|
if k in package:
|
||||||
to_fetch.append(package[k])
|
to_fetch.append(package[k])
|
||||||
elif k == 'apkName':
|
elif k == 'apkName':
|
||||||
logging.error(_('{appid} is missing {name}')
|
logging.error(
|
||||||
.format(appid=package['packageName'], name=k))
|
_('{appid} is missing {name}').format(
|
||||||
|
appid=package['packageName'], name=k
|
||||||
|
)
|
||||||
|
)
|
||||||
for f in to_fetch:
|
for f in to_fetch:
|
||||||
if not os.path.exists(f) \
|
if not os.path.exists(f) or (
|
||||||
or (f.endswith('.apk') and os.path.getsize(f) != package['size']):
|
f.endswith('.apk') and os.path.getsize(f) != package['size']
|
||||||
|
):
|
||||||
urls.append(_append_to_url_path(section, f))
|
urls.append(_append_to_url_path(section, f))
|
||||||
if options.pgp_signatures:
|
if options.pgp_signatures:
|
||||||
urls.append(_append_to_url_path(section, f + '.asc'))
|
urls.append(_append_to_url_path(section, f + '.asc'))
|
||||||
if options.build_logs and f.endswith('.apk'):
|
if options.build_logs and f.endswith('.apk'):
|
||||||
urls.append(_append_to_url_path(section, f[:-4] + '.log.gz'))
|
urls.append(
|
||||||
|
_append_to_url_path(section, f[:-4] + '.log.gz')
|
||||||
|
)
|
||||||
|
|
||||||
_run_wget(sectiondir, urls)
|
_run_wget(sectiondir, urls)
|
||||||
|
|
||||||
@ -181,7 +236,7 @@ def main():
|
|||||||
for k in update.GRAPHIC_NAMES:
|
for k in update.GRAPHIC_NAMES:
|
||||||
f = d.get(k)
|
f = d.get(k)
|
||||||
if f:
|
if f:
|
||||||
filepath_tuple = components + (f, )
|
filepath_tuple = components + (f,)
|
||||||
urls.append(_append_to_url_path(*filepath_tuple))
|
urls.append(_append_to_url_path(*filepath_tuple))
|
||||||
_run_wget(os.path.join(basedir, *components), urls)
|
_run_wget(os.path.join(basedir, *components), urls)
|
||||||
for k in update.SCREENSHOT_DIRS:
|
for k in update.SCREENSHOT_DIRS:
|
||||||
@ -190,14 +245,16 @@ def main():
|
|||||||
if filelist:
|
if filelist:
|
||||||
components = (section, app['packageName'], locale, k)
|
components = (section, app['packageName'], locale, k)
|
||||||
for f in filelist:
|
for f in filelist:
|
||||||
filepath_tuple = components + (f, )
|
filepath_tuple = components + (f,)
|
||||||
urls.append(_append_to_url_path(*filepath_tuple))
|
urls.append(_append_to_url_path(*filepath_tuple))
|
||||||
_run_wget(os.path.join(basedir, *components), urls)
|
_run_wget(os.path.join(basedir, *components), urls)
|
||||||
|
|
||||||
urls = dict()
|
urls = dict()
|
||||||
for app in data['apps']:
|
for app in data['apps']:
|
||||||
if 'icon' not in app:
|
if 'icon' not in app:
|
||||||
logging.error(_('no "icon" in {appid}').format(appid=app['packageName']))
|
logging.error(
|
||||||
|
_('no "icon" in {appid}').format(appid=app['packageName'])
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
icon = app['icon']
|
icon = app['icon']
|
||||||
for icondir in icondirs:
|
for icondir in icondirs:
|
||||||
|
@ -54,27 +54,72 @@ def _ssh_key_from_debug_keystore(keystore=KEYSTORE_FILE):
|
|||||||
p12 = os.path.join(tmp_dir, '.keystore.p12')
|
p12 = os.path.join(tmp_dir, '.keystore.p12')
|
||||||
_config = dict()
|
_config = dict()
|
||||||
common.fill_config_defaults(_config)
|
common.fill_config_defaults(_config)
|
||||||
subprocess.check_call([_config['keytool'], '-importkeystore',
|
subprocess.check_call(
|
||||||
'-srckeystore', keystore, '-srcalias', KEY_ALIAS,
|
[
|
||||||
'-srcstorepass', PASSWORD, '-srckeypass', PASSWORD,
|
_config['keytool'],
|
||||||
'-destkeystore', p12, '-destalias', KEY_ALIAS,
|
'-importkeystore',
|
||||||
'-deststorepass', PASSWORD, '-destkeypass', PASSWORD,
|
'-srckeystore',
|
||||||
'-deststoretype', 'PKCS12'],
|
keystore,
|
||||||
env={'LC_ALL': 'C.UTF-8'})
|
'-srcalias',
|
||||||
subprocess.check_call(['openssl', 'pkcs12', '-in', p12, '-out', key_pem,
|
KEY_ALIAS,
|
||||||
'-passin', 'pass:' + PASSWORD, '-passout', 'pass:' + PASSWORD],
|
'-srcstorepass',
|
||||||
env={'LC_ALL': 'C.UTF-8'})
|
PASSWORD,
|
||||||
subprocess.check_call(['openssl', 'rsa', '-in', key_pem, '-out', privkey,
|
'-srckeypass',
|
||||||
'-passin', 'pass:' + PASSWORD],
|
PASSWORD,
|
||||||
env={'LC_ALL': 'C.UTF-8'})
|
'-destkeystore',
|
||||||
|
p12,
|
||||||
|
'-destalias',
|
||||||
|
KEY_ALIAS,
|
||||||
|
'-deststorepass',
|
||||||
|
PASSWORD,
|
||||||
|
'-destkeypass',
|
||||||
|
PASSWORD,
|
||||||
|
'-deststoretype',
|
||||||
|
'PKCS12',
|
||||||
|
],
|
||||||
|
env={'LC_ALL': 'C.UTF-8'},
|
||||||
|
)
|
||||||
|
subprocess.check_call(
|
||||||
|
[
|
||||||
|
'openssl',
|
||||||
|
'pkcs12',
|
||||||
|
'-in',
|
||||||
|
p12,
|
||||||
|
'-out',
|
||||||
|
key_pem,
|
||||||
|
'-passin',
|
||||||
|
'pass:' + PASSWORD,
|
||||||
|
'-passout',
|
||||||
|
'pass:' + PASSWORD,
|
||||||
|
],
|
||||||
|
env={'LC_ALL': 'C.UTF-8'},
|
||||||
|
)
|
||||||
|
subprocess.check_call(
|
||||||
|
[
|
||||||
|
'openssl',
|
||||||
|
'rsa',
|
||||||
|
'-in',
|
||||||
|
key_pem,
|
||||||
|
'-out',
|
||||||
|
privkey,
|
||||||
|
'-passin',
|
||||||
|
'pass:' + PASSWORD,
|
||||||
|
],
|
||||||
|
env={'LC_ALL': 'C.UTF-8'},
|
||||||
|
)
|
||||||
os.remove(key_pem)
|
os.remove(key_pem)
|
||||||
os.remove(p12)
|
os.remove(p12)
|
||||||
os.chmod(privkey, 0o600) # os.umask() should cover this, but just in case
|
os.chmod(privkey, 0o600) # os.umask() should cover this, but just in case
|
||||||
|
|
||||||
rsakey = paramiko.RSAKey.from_private_key_file(privkey)
|
rsakey = paramiko.RSAKey.from_private_key_file(privkey)
|
||||||
fingerprint = base64.b64encode(hashlib.sha256(rsakey.asbytes()).digest()).decode('ascii').rstrip('=')
|
fingerprint = (
|
||||||
ssh_private_key_file = os.path.join(tmp_dir, 'debug_keystore_'
|
base64.b64encode(hashlib.sha256(rsakey.asbytes()).digest())
|
||||||
+ fingerprint.replace('/', '_') + '_id_rsa')
|
.decode('ascii')
|
||||||
|
.rstrip('=')
|
||||||
|
)
|
||||||
|
ssh_private_key_file = os.path.join(
|
||||||
|
tmp_dir, 'debug_keystore_' + fingerprint.replace('/', '_') + '_id_rsa'
|
||||||
|
)
|
||||||
shutil.move(privkey, ssh_private_key_file)
|
shutil.move(privkey, ssh_private_key_file)
|
||||||
|
|
||||||
pub = rsakey.get_name() + ' ' + rsakey.get_base64() + ' ' + ssh_private_key_file
|
pub = rsakey.get_name() + ' ' + rsakey.get_base64() + ' ' + ssh_private_key_file
|
||||||
@ -90,20 +135,46 @@ def main():
|
|||||||
|
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
common.setup_global_opts(parser)
|
common.setup_global_opts(parser)
|
||||||
parser.add_argument("--keystore", default=KEYSTORE_FILE,
|
parser.add_argument(
|
||||||
help=_("Specify which debug keystore file to use."))
|
"--keystore",
|
||||||
parser.add_argument("--show-secret-var", action="store_true", default=False,
|
default=KEYSTORE_FILE,
|
||||||
help=_("Print the secret variable to the terminal for easy copy/paste"))
|
help=_("Specify which debug keystore file to use."),
|
||||||
parser.add_argument("--keep-private-keys", action="store_true", default=False,
|
)
|
||||||
help=_("Do not remove the private keys generated from the keystore"))
|
parser.add_argument(
|
||||||
parser.add_argument("--no-deploy", action="store_true", default=False,
|
"--show-secret-var",
|
||||||
help=_("Do not deploy the new files to the repo"))
|
action="store_true",
|
||||||
parser.add_argument("--file", default='app/build/outputs/apk/*.apk',
|
default=False,
|
||||||
help=_('The file to be included in the repo (path or glob)'))
|
help=_("Print the secret variable to the terminal for easy copy/paste"),
|
||||||
parser.add_argument("--no-checksum", action="store_true", default=False,
|
)
|
||||||
help=_("Don't use rsync checksums"))
|
parser.add_argument(
|
||||||
parser.add_argument("--archive-older", type=int, default=20,
|
"--keep-private-keys",
|
||||||
help=_("Set maximum releases in repo before older ones are archived"))
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help=_("Do not remove the private keys generated from the keystore"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-deploy",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help=_("Do not deploy the new files to the repo"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--file",
|
||||||
|
default='app/build/outputs/apk/*.apk',
|
||||||
|
help=_('The file to be included in the repo (path or glob)'),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-checksum",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help=_("Don't use rsync checksums"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--archive-older",
|
||||||
|
type=int,
|
||||||
|
default=20,
|
||||||
|
help=_("Set maximum releases in repo before older ones are archived"),
|
||||||
|
)
|
||||||
# TODO add --with-btlog
|
# TODO add --with-btlog
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
@ -149,9 +220,11 @@ def main():
|
|||||||
+ '\nhttps://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys')
|
+ '\nhttps://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys')
|
||||||
git_user_name = repo_git_base
|
git_user_name = repo_git_base
|
||||||
git_user_email = os.getenv('USER') + '@' + platform.node()
|
git_user_email = os.getenv('USER') + '@' + platform.node()
|
||||||
elif 'CIRCLE_REPOSITORY_URL' in os.environ \
|
elif (
|
||||||
and 'CIRCLE_PROJECT_USERNAME' in os.environ \
|
'CIRCLE_REPOSITORY_URL' in os.environ
|
||||||
and 'CIRCLE_PROJECT_REPONAME' in os.environ:
|
and 'CIRCLE_PROJECT_USERNAME' in os.environ
|
||||||
|
and 'CIRCLE_PROJECT_REPONAME' in os.environ
|
||||||
|
):
|
||||||
# we are in Circle CI
|
# we are in Circle CI
|
||||||
repo_git_base = (os.getenv('CIRCLE_PROJECT_USERNAME')
|
repo_git_base = (os.getenv('CIRCLE_PROJECT_USERNAME')
|
||||||
+ '/' + os.getenv('CIRCLE_PROJECT_REPONAME') + NIGHTLY)
|
+ '/' + os.getenv('CIRCLE_PROJECT_REPONAME') + NIGHTLY)
|
||||||
@ -229,7 +302,9 @@ Last updated: {date}'''.format(repo_git_base=repo_git_base,
|
|||||||
config += "repo_description = 'Nightly builds from %s'\n" % git_user_email
|
config += "repo_description = 'Nightly builds from %s'\n" % git_user_email
|
||||||
config += "archive_name = '%s'\n" % (repo_git_base + ' archive')
|
config += "archive_name = '%s'\n" % (repo_git_base + ' archive')
|
||||||
config += "archive_url = '%s'\n" % (repo_base + '/archive')
|
config += "archive_url = '%s'\n" % (repo_base + '/archive')
|
||||||
config += "archive_description = 'Old nightly builds that have been archived.'\n"
|
config += (
|
||||||
|
"archive_description = 'Old nightly builds that have been archived.'\n"
|
||||||
|
)
|
||||||
config += "archive_older = %i\n" % options.archive_older
|
config += "archive_older = %i\n" % options.archive_older
|
||||||
config += "servergitmirrors = '%s'\n" % servergitmirror
|
config += "servergitmirrors = '%s'\n" % servergitmirror
|
||||||
config += "keystore = '%s'\n" % KEYSTORE_FILE
|
config += "keystore = '%s'\n" % KEYSTORE_FILE
|
||||||
@ -252,25 +327,38 @@ Last updated: {date}'''.format(repo_git_base=repo_git_base,
|
|||||||
for f in files:
|
for f in files:
|
||||||
if f.endswith('-debug.apk'):
|
if f.endswith('-debug.apk'):
|
||||||
apkfilename = os.path.join(root, f)
|
apkfilename = os.path.join(root, f)
|
||||||
logging.debug(_('Stripping mystery signature from {apkfilename}')
|
logging.debug(
|
||||||
.format(apkfilename=apkfilename))
|
_('Stripping mystery signature from {apkfilename}').format(
|
||||||
|
apkfilename=apkfilename
|
||||||
|
)
|
||||||
|
)
|
||||||
destapk = os.path.join(repodir, os.path.basename(f))
|
destapk = os.path.join(repodir, os.path.basename(f))
|
||||||
os.chmod(apkfilename, 0o644)
|
os.chmod(apkfilename, 0o644)
|
||||||
logging.debug(_('Resigning {apkfilename} with provided debug.keystore')
|
logging.debug(
|
||||||
.format(apkfilename=os.path.basename(apkfilename)))
|
_(
|
||||||
|
'Resigning {apkfilename} with provided debug.keystore'
|
||||||
|
).format(apkfilename=os.path.basename(apkfilename))
|
||||||
|
)
|
||||||
common.apk_strip_v1_signatures(apkfilename, strip_manifest=True)
|
common.apk_strip_v1_signatures(apkfilename, strip_manifest=True)
|
||||||
common.sign_apk(apkfilename, destapk, KEY_ALIAS)
|
common.sign_apk(apkfilename, destapk, KEY_ALIAS)
|
||||||
|
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
logging.debug(_('attempting bare SSH connection to test deploy key:'))
|
logging.debug(_('attempting bare SSH connection to test deploy key:'))
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(['ssh', '-Tvi', ssh_private_key_file,
|
subprocess.check_call(
|
||||||
'-oIdentitiesOnly=yes', '-oStrictHostKeyChecking=no',
|
[
|
||||||
servergitmirror.split(':')[0]])
|
'ssh',
|
||||||
|
'-Tvi',
|
||||||
|
ssh_private_key_file,
|
||||||
|
'-oIdentitiesOnly=yes',
|
||||||
|
'-oStrictHostKeyChecking=no',
|
||||||
|
servergitmirror.split(':')[0],
|
||||||
|
]
|
||||||
|
)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
app_url = clone_url[:-len(NIGHTLY)]
|
app_url = clone_url[: -len(NIGHTLY)]
|
||||||
template = dict()
|
template = dict()
|
||||||
template['AuthorName'] = clone_url.split('/')[4]
|
template['AuthorName'] = clone_url.split('/')[4]
|
||||||
template['AuthorWebSite'] = '/'.join(clone_url.split('/')[:4])
|
template['AuthorWebSite'] = '/'.join(clone_url.split('/')[:4])
|
||||||
@ -282,9 +370,13 @@ Last updated: {date}'''.format(repo_git_base=repo_git_base,
|
|||||||
with open('template.yml', 'w') as fp:
|
with open('template.yml', 'w') as fp:
|
||||||
yaml.dump(template, fp)
|
yaml.dump(template, fp)
|
||||||
|
|
||||||
subprocess.check_call(['fdroid', 'update', '--rename-apks', '--create-metadata', '--verbose'],
|
subprocess.check_call(
|
||||||
cwd=repo_basedir)
|
['fdroid', 'update', '--rename-apks', '--create-metadata', '--verbose'],
|
||||||
common.local_rsync(options, repo_basedir + '/metadata/', git_mirror_metadatadir + '/')
|
cwd=repo_basedir,
|
||||||
|
)
|
||||||
|
common.local_rsync(
|
||||||
|
options, repo_basedir + '/metadata/', git_mirror_metadatadir + '/'
|
||||||
|
)
|
||||||
common.local_rsync(options, repo_basedir + '/stats/', git_mirror_statsdir + '/')
|
common.local_rsync(options, repo_basedir + '/stats/', git_mirror_statsdir + '/')
|
||||||
mirror_git_repo.git.add(all=True)
|
mirror_git_repo.git.add(all=True)
|
||||||
mirror_git_repo.index.commit("update app metadata")
|
mirror_git_repo.index.commit("update app metadata")
|
||||||
@ -294,8 +386,11 @@ Last updated: {date}'''.format(repo_git_base=repo_git_base,
|
|||||||
cmd = ['fdroid', 'deploy', '--verbose', '--no-keep-git-mirror-archive']
|
cmd = ['fdroid', 'deploy', '--verbose', '--no-keep-git-mirror-archive']
|
||||||
subprocess.check_call(cmd, cwd=repo_basedir)
|
subprocess.check_call(cmd, cwd=repo_basedir)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logging.error(_('cannot publish update, did you set the deploy key?')
|
logging.error(
|
||||||
+ '\n' + deploy_key_url)
|
_('cannot publish update, did you set the deploy key?')
|
||||||
|
+ '\n'
|
||||||
|
+ deploy_key_url
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not options.keep_private_keys:
|
if not options.keep_private_keys:
|
||||||
@ -326,8 +421,11 @@ Last updated: {date}'''.format(repo_git_base=repo_git_base,
|
|||||||
if options.show_secret_var:
|
if options.show_secret_var:
|
||||||
with open(options.keystore, 'rb') as fp:
|
with open(options.keystore, 'rb') as fp:
|
||||||
debug_keystore = base64.standard_b64encode(fp.read()).decode('ascii')
|
debug_keystore = base64.standard_b64encode(fp.read()).decode('ascii')
|
||||||
print(_('\n{path} encoded for the DEBUG_KEYSTORE secret variable:')
|
print(
|
||||||
.format(path=options.keystore))
|
_('\n{path} encoded for the DEBUG_KEYSTORE secret variable:').format(
|
||||||
|
path=options.keystore
|
||||||
|
)
|
||||||
|
)
|
||||||
print(debug_keystore)
|
print(debug_keystore)
|
||||||
|
|
||||||
os.umask(umask)
|
os.umask(umask)
|
||||||
|
@ -76,11 +76,16 @@ def key_alias(appid):
|
|||||||
|
|
||||||
def read_fingerprints_from_keystore():
|
def read_fingerprints_from_keystore():
|
||||||
"""Obtain a dictionary containing all singning-key fingerprints which are managed by F-Droid, grouped by appid."""
|
"""Obtain a dictionary containing all singning-key fingerprints which are managed by F-Droid, grouped by appid."""
|
||||||
env_vars = {'LC_ALL': 'C.UTF-8',
|
env_vars = {'LC_ALL': 'C.UTF-8', 'FDROID_KEY_STORE_PASS': config['keystorepass']}
|
||||||
'FDROID_KEY_STORE_PASS': config['keystorepass']}
|
cmd = [
|
||||||
cmd = [config['keytool'], '-list',
|
config['keytool'],
|
||||||
'-v', '-keystore', config['keystore'],
|
'-list',
|
||||||
'-storepass:env', 'FDROID_KEY_STORE_PASS']
|
'-v',
|
||||||
|
'-keystore',
|
||||||
|
config['keystore'],
|
||||||
|
'-storepass:env',
|
||||||
|
'FDROID_KEY_STORE_PASS',
|
||||||
|
]
|
||||||
if config['keystore'] == 'NONE':
|
if config['keystore'] == 'NONE':
|
||||||
cmd += config['smartcardoptions']
|
cmd += config['smartcardoptions']
|
||||||
p = FDroidPopen(cmd, envs=env_vars, output=False)
|
p = FDroidPopen(cmd, envs=env_vars, output=False)
|
||||||
@ -116,8 +121,10 @@ def sign_sig_key_fingerprint_list(jar_file):
|
|||||||
cmd += config['smartcardoptions']
|
cmd += config['smartcardoptions']
|
||||||
else: # smardcards never use -keypass
|
else: # smardcards never use -keypass
|
||||||
cmd += '-keypass:env', 'FDROID_KEY_PASS'
|
cmd += '-keypass:env', 'FDROID_KEY_PASS'
|
||||||
env_vars = {'FDROID_KEY_STORE_PASS': config['keystorepass'],
|
env_vars = {
|
||||||
'FDROID_KEY_PASS': config.get('keypass', "")}
|
'FDROID_KEY_STORE_PASS': config['keystorepass'],
|
||||||
|
'FDROID_KEY_PASS': config.get('keypass', ""),
|
||||||
|
}
|
||||||
p = common.FDroidPopen(cmd, envs=env_vars)
|
p = common.FDroidPopen(cmd, envs=env_vars)
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise FDroidException("Failed to sign '{}'!".format(jar_file))
|
raise FDroidException("Failed to sign '{}'!".format(jar_file))
|
||||||
@ -201,24 +208,44 @@ def create_key_if_not_existing(keyalias):
|
|||||||
"""
|
"""
|
||||||
# See if we already have a key for this application, and
|
# See if we already have a key for this application, and
|
||||||
# if not generate one...
|
# if not generate one...
|
||||||
env_vars = {'LC_ALL': 'C.UTF-8',
|
env_vars = {
|
||||||
'FDROID_KEY_STORE_PASS': config['keystorepass'],
|
'LC_ALL': 'C.UTF-8',
|
||||||
'FDROID_KEY_PASS': config.get('keypass', "")}
|
'FDROID_KEY_STORE_PASS': config['keystorepass'],
|
||||||
cmd = [config['keytool'], '-list',
|
'FDROID_KEY_PASS': config.get('keypass', ""),
|
||||||
'-alias', keyalias, '-keystore', config['keystore'],
|
}
|
||||||
'-storepass:env', 'FDROID_KEY_STORE_PASS']
|
cmd = [
|
||||||
|
config['keytool'],
|
||||||
|
'-list',
|
||||||
|
'-alias',
|
||||||
|
keyalias,
|
||||||
|
'-keystore',
|
||||||
|
config['keystore'],
|
||||||
|
'-storepass:env',
|
||||||
|
'FDROID_KEY_STORE_PASS',
|
||||||
|
]
|
||||||
if config['keystore'] == 'NONE':
|
if config['keystore'] == 'NONE':
|
||||||
cmd += config['smartcardoptions']
|
cmd += config['smartcardoptions']
|
||||||
p = FDroidPopen(cmd, envs=env_vars)
|
p = FDroidPopen(cmd, envs=env_vars)
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
logging.info("Key does not exist - generating...")
|
logging.info("Key does not exist - generating...")
|
||||||
cmd = [config['keytool'], '-genkey',
|
cmd = [
|
||||||
'-keystore', config['keystore'],
|
config['keytool'],
|
||||||
'-alias', keyalias,
|
'-genkey',
|
||||||
'-keyalg', 'RSA', '-keysize', '2048',
|
'-keystore',
|
||||||
'-validity', '10000',
|
config['keystore'],
|
||||||
'-storepass:env', 'FDROID_KEY_STORE_PASS',
|
'-alias',
|
||||||
'-dname', config['keydname']]
|
keyalias,
|
||||||
|
'-keyalg',
|
||||||
|
'RSA',
|
||||||
|
'-keysize',
|
||||||
|
'2048',
|
||||||
|
'-validity',
|
||||||
|
'10000',
|
||||||
|
'-storepass:env',
|
||||||
|
'FDROID_KEY_STORE_PASS',
|
||||||
|
'-dname',
|
||||||
|
config['keydname'],
|
||||||
|
]
|
||||||
if config['keystore'] == 'NONE':
|
if config['keystore'] == 'NONE':
|
||||||
cmd += config['smartcardoptions']
|
cmd += config['smartcardoptions']
|
||||||
else:
|
else:
|
||||||
@ -235,11 +262,15 @@ def main():
|
|||||||
global config, options
|
global config, options
|
||||||
|
|
||||||
# Parse command line...
|
# Parse command line...
|
||||||
parser = ArgumentParser(usage="%(prog)s [options] "
|
parser = ArgumentParser(
|
||||||
"[APPID[:VERCODE] [APPID[:VERCODE] ...]]")
|
usage="%(prog)s [options] " "[APPID[:VERCODE] [APPID[:VERCODE] ...]]"
|
||||||
|
)
|
||||||
common.setup_global_opts(parser)
|
common.setup_global_opts(parser)
|
||||||
parser.add_argument("appid", nargs='*',
|
parser.add_argument(
|
||||||
help=_("application ID with optional versionCode in the form APPID[:VERCODE]"))
|
"appid",
|
||||||
|
nargs='*',
|
||||||
|
help=_("application ID with optional versionCode in the form APPID[:VERCODE]"),
|
||||||
|
)
|
||||||
metadata.add_metadata_arguments(parser)
|
metadata.add_metadata_arguments(parser)
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
metadata.warnings_action = options.W
|
metadata.warnings_action = options.W
|
||||||
@ -247,7 +278,9 @@ def main():
|
|||||||
config = common.read_config(options)
|
config = common.read_config(options)
|
||||||
|
|
||||||
if not ('jarsigner' in config and 'keytool' in config):
|
if not ('jarsigner' in config and 'keytool' in config):
|
||||||
logging.critical(_('Java JDK not found! Install in standard location or set java_paths!'))
|
logging.critical(
|
||||||
|
_('Java JDK not found! Install in standard location or set java_paths!')
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
common.assert_config_keystore(config)
|
common.assert_config_keystore(config)
|
||||||
@ -279,16 +312,21 @@ def main():
|
|||||||
|
|
||||||
allapps = metadata.read_metadata()
|
allapps = metadata.read_metadata()
|
||||||
vercodes = common.read_pkg_args(options.appid, True)
|
vercodes = common.read_pkg_args(options.appid, True)
|
||||||
common.get_metadata_files(vercodes) # only check appids
|
common.get_metadata_files(vercodes) # only check appids
|
||||||
signed_apks = dict()
|
signed_apks = dict()
|
||||||
generated_keys = dict()
|
generated_keys = dict()
|
||||||
allaliases = check_for_key_collisions(allapps)
|
allaliases = check_for_key_collisions(allapps)
|
||||||
logging.info(ngettext('{0} app, {1} key aliases',
|
logging.info(
|
||||||
'{0} apps, {1} key aliases', len(allapps)).format(len(allapps), len(allaliases)))
|
ngettext(
|
||||||
|
'{0} app, {1} key aliases', '{0} apps, {1} key aliases', len(allapps)
|
||||||
|
).format(len(allapps), len(allaliases))
|
||||||
|
)
|
||||||
|
|
||||||
# Process any APKs or ZIPs that are waiting to be signed...
|
# Process any APKs or ZIPs that are waiting to be signed...
|
||||||
for apkfile in sorted(glob.glob(os.path.join(unsigned_dir, '*.apk'))
|
for apkfile in sorted(
|
||||||
+ glob.glob(os.path.join(unsigned_dir, '*.zip'))):
|
glob.glob(os.path.join(unsigned_dir, '*.apk'))
|
||||||
|
+ glob.glob(os.path.join(unsigned_dir, '*.zip'))
|
||||||
|
):
|
||||||
|
|
||||||
appid, vercode = common.publishednameinfo(apkfile)
|
appid, vercode = common.publishednameinfo(apkfile)
|
||||||
apkfilename = os.path.basename(apkfile)
|
apkfilename = os.path.basename(apkfile)
|
||||||
@ -302,8 +340,9 @@ def main():
|
|||||||
# There ought to be valid metadata for this app, otherwise why are we
|
# There ought to be valid metadata for this app, otherwise why are we
|
||||||
# trying to publish it?
|
# trying to publish it?
|
||||||
if appid not in allapps:
|
if appid not in allapps:
|
||||||
logging.error("Unexpected {0} found in unsigned directory"
|
logging.error(
|
||||||
.format(apkfilename))
|
"Unexpected {0} found in unsigned directory".format(apkfilename)
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
app = allapps[appid]
|
app = allapps[appid]
|
||||||
|
|
||||||
@ -359,7 +398,9 @@ def main():
|
|||||||
signature_file, _ignored, manifest, v2_files = signingfiles
|
signature_file, _ignored, manifest, v2_files = signingfiles
|
||||||
|
|
||||||
with open(signature_file, 'rb') as f:
|
with open(signature_file, 'rb') as f:
|
||||||
devfp = common.signer_fingerprint_short(common.get_certificate(f.read()))
|
devfp = common.signer_fingerprint_short(
|
||||||
|
common.get_certificate(f.read())
|
||||||
|
)
|
||||||
devsigned = '{}_{}_{}.apk'.format(appid, vercode, devfp)
|
devsigned = '{}_{}_{}.apk'.format(appid, vercode, devfp)
|
||||||
devsignedtmp = os.path.join(tmp_dir, devsigned)
|
devsignedtmp = os.path.join(tmp_dir, devsigned)
|
||||||
|
|
||||||
@ -390,8 +431,7 @@ def main():
|
|||||||
common.sign_apk(apkfile, signed_apk_path, keyalias)
|
common.sign_apk(apkfile, signed_apk_path, keyalias)
|
||||||
if appid not in signed_apks:
|
if appid not in signed_apks:
|
||||||
signed_apks[appid] = []
|
signed_apks[appid] = []
|
||||||
signed_apks[appid].append({"keyalias": keyalias,
|
signed_apks[appid].append({"keyalias": keyalias, "filename": apkfile})
|
||||||
"filename": apkfile})
|
|
||||||
|
|
||||||
publish_source_tarball(apkfilename, unsigned_dir, output_dir)
|
publish_source_tarball(apkfilename, unsigned_dir, output_dir)
|
||||||
logging.info('Published ' + apkfilename)
|
logging.info('Published ' + apkfilename)
|
||||||
|
@ -89,8 +89,9 @@ def extract(options):
|
|||||||
def main():
|
def main():
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
common.setup_global_opts(parser)
|
common.setup_global_opts(parser)
|
||||||
parser.add_argument("APK", nargs='*',
|
parser.add_argument(
|
||||||
help=_("signed APK, either a file-path or HTTPS URL."))
|
"APK", nargs='*', help=_("signed APK, either a file-path or HTTPS URL.")
|
||||||
|
)
|
||||||
parser.add_argument("--no-check-https", action="store_true", default=False)
|
parser.add_argument("--no-check-https", action="store_true", default=False)
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
|
@ -41,10 +41,19 @@ def sign_jar(jar):
|
|||||||
but then Android < 4.3 would not be able to verify it.
|
but then Android < 4.3 would not be able to verify it.
|
||||||
https://code.google.com/p/android/issues/detail?id=38321
|
https://code.google.com/p/android/issues/detail?id=38321
|
||||||
"""
|
"""
|
||||||
args = [config['jarsigner'], '-keystore', config['keystore'],
|
args = [
|
||||||
'-storepass:env', 'FDROID_KEY_STORE_PASS',
|
config['jarsigner'],
|
||||||
'-digestalg', 'SHA1', '-sigalg', 'SHA1withRSA',
|
'-keystore',
|
||||||
jar, config['repo_keyalias']]
|
config['keystore'],
|
||||||
|
'-storepass:env',
|
||||||
|
'FDROID_KEY_STORE_PASS',
|
||||||
|
'-digestalg',
|
||||||
|
'SHA1',
|
||||||
|
'-sigalg',
|
||||||
|
'SHA1withRSA',
|
||||||
|
jar,
|
||||||
|
config['repo_keyalias'],
|
||||||
|
]
|
||||||
if config['keystore'] == 'NONE':
|
if config['keystore'] == 'NONE':
|
||||||
args += config['smartcardoptions']
|
args += config['smartcardoptions']
|
||||||
else: # smardcards never use -keypass
|
else: # smardcards never use -keypass
|
||||||
@ -96,7 +105,9 @@ def main():
|
|||||||
|
|
||||||
if 'jarsigner' not in config:
|
if 'jarsigner' not in config:
|
||||||
raise FDroidException(
|
raise FDroidException(
|
||||||
_('Java jarsigner not found! Install in standard location or set java_paths!')
|
_(
|
||||||
|
'Java jarsigner not found! Install in standard location or set java_paths!'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
repodirs = ['repo']
|
repodirs = ['repo']
|
||||||
|
@ -42,14 +42,18 @@ def get_clean_builder(serverdir):
|
|||||||
vagrantfile = os.path.join(serverdir, 'Vagrantfile')
|
vagrantfile = os.path.join(serverdir, 'Vagrantfile')
|
||||||
if not os.path.isfile(vagrantfile):
|
if not os.path.isfile(vagrantfile):
|
||||||
with open(vagrantfile, 'w') as f:
|
with open(vagrantfile, 'w') as f:
|
||||||
f.write(textwrap.dedent("""\
|
f.write(
|
||||||
|
textwrap.dedent(
|
||||||
|
"""\
|
||||||
# generated file, do not change.
|
# generated file, do not change.
|
||||||
|
|
||||||
Vagrant.configure("2") do |config|
|
Vagrant.configure("2") do |config|
|
||||||
config.vm.box = "buildserver"
|
config.vm.box = "buildserver"
|
||||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
end
|
end
|
||||||
"""))
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
vm = get_build_vm(serverdir)
|
vm = get_build_vm(serverdir)
|
||||||
logging.info('destroying buildserver before build')
|
logging.info('destroying buildserver before build')
|
||||||
vm.destroy()
|
vm.destroy()
|
||||||
@ -119,25 +123,37 @@ def get_build_vm(srvdir, provider=None):
|
|||||||
if kvm_installed and vbox_installed:
|
if kvm_installed and vbox_installed:
|
||||||
logging.debug('both kvm and vbox are installed.')
|
logging.debug('both kvm and vbox are installed.')
|
||||||
elif kvm_installed:
|
elif kvm_installed:
|
||||||
logging.debug('libvirt is the sole installed and supported vagrant provider, selecting \'libvirt\'')
|
logging.debug(
|
||||||
|
'libvirt is the sole installed and supported vagrant provider, selecting \'libvirt\''
|
||||||
|
)
|
||||||
return LibvirtBuildVm(abssrvdir)
|
return LibvirtBuildVm(abssrvdir)
|
||||||
elif vbox_installed:
|
elif vbox_installed:
|
||||||
logging.debug('virtualbox is the sole installed and supported vagrant provider, selecting \'virtualbox\'')
|
logging.debug(
|
||||||
|
'virtualbox is the sole installed and supported vagrant provider, selecting \'virtualbox\''
|
||||||
|
)
|
||||||
return VirtualboxBuildVm(abssrvdir)
|
return VirtualboxBuildVm(abssrvdir)
|
||||||
else:
|
else:
|
||||||
logging.debug('could not confirm that either virtualbox or kvm/libvirt are installed')
|
logging.debug(
|
||||||
|
'could not confirm that either virtualbox or kvm/libvirt are installed'
|
||||||
|
)
|
||||||
|
|
||||||
# try guessing provider from .../srvdir/.vagrant internals
|
# try guessing provider from .../srvdir/.vagrant internals
|
||||||
vagrant_libvirt_path = os.path.join(abssrvdir, '.vagrant', 'machines',
|
vagrant_libvirt_path = os.path.join(
|
||||||
'default', 'libvirt')
|
abssrvdir, '.vagrant', 'machines', 'default', 'libvirt'
|
||||||
has_libvirt_machine = isdir(vagrant_libvirt_path) \
|
)
|
||||||
and len(os.listdir(vagrant_libvirt_path)) > 0
|
has_libvirt_machine = (
|
||||||
vagrant_virtualbox_path = os.path.join(abssrvdir, '.vagrant', 'machines',
|
isdir(vagrant_libvirt_path) and len(os.listdir(vagrant_libvirt_path)) > 0
|
||||||
'default', 'virtualbox')
|
)
|
||||||
has_vbox_machine = isdir(vagrant_virtualbox_path) \
|
vagrant_virtualbox_path = os.path.join(
|
||||||
and len(os.listdir(vagrant_virtualbox_path)) > 0
|
abssrvdir, '.vagrant', 'machines', 'default', 'virtualbox'
|
||||||
|
)
|
||||||
|
has_vbox_machine = (
|
||||||
|
isdir(vagrant_virtualbox_path) and len(os.listdir(vagrant_virtualbox_path)) > 0
|
||||||
|
)
|
||||||
if has_libvirt_machine and has_vbox_machine:
|
if has_libvirt_machine and has_vbox_machine:
|
||||||
logging.info('build vm provider lookup found virtualbox and libvirt, defaulting to \'virtualbox\'')
|
logging.info(
|
||||||
|
'build vm provider lookup found virtualbox and libvirt, defaulting to \'virtualbox\''
|
||||||
|
)
|
||||||
return VirtualboxBuildVm(abssrvdir)
|
return VirtualboxBuildVm(abssrvdir)
|
||||||
elif has_libvirt_machine:
|
elif has_libvirt_machine:
|
||||||
logging.debug('build vm provider lookup found \'libvirt\'')
|
logging.debug('build vm provider lookup found \'libvirt\'')
|
||||||
@ -149,12 +165,15 @@ def get_build_vm(srvdir, provider=None):
|
|||||||
# try guessing provider from available buildserver boxes
|
# try guessing provider from available buildserver boxes
|
||||||
available_boxes = []
|
available_boxes = []
|
||||||
import vagrant
|
import vagrant
|
||||||
|
|
||||||
boxes = vagrant.Vagrant().box_list()
|
boxes = vagrant.Vagrant().box_list()
|
||||||
for box in boxes:
|
for box in boxes:
|
||||||
if box.name == "buildserver":
|
if box.name == "buildserver":
|
||||||
available_boxes.append(box.provider)
|
available_boxes.append(box.provider)
|
||||||
if "libvirt" in available_boxes and "virtualbox" in available_boxes:
|
if "libvirt" in available_boxes and "virtualbox" in available_boxes:
|
||||||
logging.info('basebox lookup found virtualbox and libvirt boxes, defaulting to \'virtualbox\'')
|
logging.info(
|
||||||
|
'basebox lookup found virtualbox and libvirt boxes, defaulting to \'virtualbox\''
|
||||||
|
)
|
||||||
return VirtualboxBuildVm(abssrvdir)
|
return VirtualboxBuildVm(abssrvdir)
|
||||||
elif "libvirt" in available_boxes:
|
elif "libvirt" in available_boxes:
|
||||||
logging.info('\'libvirt\' buildserver box available, using that')
|
logging.info('\'libvirt\' buildserver box available, using that')
|
||||||
@ -171,7 +190,7 @@ class FDroidBuildVmException(FDroidException):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FDroidBuildVm():
|
class FDroidBuildVm:
|
||||||
"""Abstract base class for working with FDroids build-servers.
|
"""Abstract base class for working with FDroids build-servers.
|
||||||
|
|
||||||
Use the factory method `fdroidserver.vmtools.get_build_vm()` for
|
Use the factory method `fdroidserver.vmtools.get_build_vm()` for
|
||||||
@ -188,11 +207,18 @@ class FDroidBuildVm():
|
|||||||
self.vgrntfile = os.path.join(srvdir, 'Vagrantfile')
|
self.vgrntfile = os.path.join(srvdir, 'Vagrantfile')
|
||||||
self.srvuuid = self._vagrant_fetch_uuid()
|
self.srvuuid = self._vagrant_fetch_uuid()
|
||||||
if not isdir(srvdir):
|
if not isdir(srvdir):
|
||||||
raise FDroidBuildVmException("Can not init vagrant, directory %s not present" % (srvdir))
|
raise FDroidBuildVmException(
|
||||||
|
"Can not init vagrant, directory %s not present" % (srvdir)
|
||||||
|
)
|
||||||
if not isfile(self.vgrntfile):
|
if not isfile(self.vgrntfile):
|
||||||
raise FDroidBuildVmException("Can not init vagrant, '%s' not present" % (self.vgrntfile))
|
raise FDroidBuildVmException(
|
||||||
|
"Can not init vagrant, '%s' not present" % (self.vgrntfile)
|
||||||
|
)
|
||||||
import vagrant
|
import vagrant
|
||||||
self.vgrnt = vagrant.Vagrant(root=srvdir, out_cm=vagrant.stdout_cm, err_cm=vagrant.stdout_cm)
|
|
||||||
|
self.vgrnt = vagrant.Vagrant(
|
||||||
|
root=srvdir, out_cm=vagrant.stdout_cm, err_cm=vagrant.stdout_cm
|
||||||
|
)
|
||||||
|
|
||||||
def up(self, provision=True):
|
def up(self, provision=True):
|
||||||
global lock
|
global lock
|
||||||
@ -302,7 +328,9 @@ class FDroidBuildVm():
|
|||||||
"""
|
"""
|
||||||
boxfile = abspath(boxfile)
|
boxfile = abspath(boxfile)
|
||||||
if not isfile(boxfile):
|
if not isfile(boxfile):
|
||||||
raise FDroidBuildVmException('supplied boxfile \'%s\' does not exist', boxfile)
|
raise FDroidBuildVmException(
|
||||||
|
'supplied boxfile \'%s\' does not exist', boxfile
|
||||||
|
)
|
||||||
self.vgrnt.box_add(boxname, abspath(boxfile), force=force)
|
self.vgrnt.box_add(boxname, abspath(boxfile), force=force)
|
||||||
|
|
||||||
def box_remove(self, boxname):
|
def box_remove(self, boxname):
|
||||||
@ -310,11 +338,13 @@ class FDroidBuildVm():
|
|||||||
_check_call(['vagrant', 'box', 'remove', '--all', '--force', boxname])
|
_check_call(['vagrant', 'box', 'remove', '--all', '--force', boxname])
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logging.debug('tried removing box %s, but is did not exist: %s', boxname, e)
|
logging.debug('tried removing box %s, but is did not exist: %s', boxname, e)
|
||||||
boxpath = os.path.join(expanduser('~'), '.vagrant',
|
boxpath = os.path.join(
|
||||||
self._vagrant_file_name(boxname))
|
expanduser('~'), '.vagrant', self._vagrant_file_name(boxname)
|
||||||
|
)
|
||||||
if isdir(boxpath):
|
if isdir(boxpath):
|
||||||
logging.info("attempting to remove box '%s' by deleting: %s",
|
logging.info(
|
||||||
boxname, boxpath)
|
"attempting to remove box '%s' by deleting: %s", boxname, boxpath
|
||||||
|
)
|
||||||
shutil.rmtree(boxpath)
|
shutil.rmtree(boxpath)
|
||||||
|
|
||||||
def sshinfo(self):
|
def sshinfo(self):
|
||||||
@ -325,11 +355,11 @@ class FDroidBuildVm():
|
|||||||
A dictionary containing 'hostname', 'port', 'user' and 'idfile'
|
A dictionary containing 'hostname', 'port', 'user' and 'idfile'
|
||||||
"""
|
"""
|
||||||
import paramiko
|
import paramiko
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sshconfig_path = os.path.join(self.srvdir, 'sshconfig')
|
sshconfig_path = os.path.join(self.srvdir, 'sshconfig')
|
||||||
with open(sshconfig_path, 'wb') as fp:
|
with open(sshconfig_path, 'wb') as fp:
|
||||||
fp.write(_check_output(['vagrant', 'ssh-config'],
|
fp.write(_check_output(['vagrant', 'ssh-config'], cwd=self.srvdir))
|
||||||
cwd=self.srvdir))
|
|
||||||
vagranthost = 'default' # Host in ssh config file
|
vagranthost = 'default' # Host in ssh config file
|
||||||
sshconfig = paramiko.SSHConfig()
|
sshconfig = paramiko.SSHConfig()
|
||||||
with open(sshconfig_path, 'r') as f:
|
with open(sshconfig_path, 'r') as f:
|
||||||
@ -340,10 +370,12 @@ class FDroidBuildVm():
|
|||||||
idfile = idfile[0]
|
idfile = idfile[0]
|
||||||
elif idfile.startswith('"') and idfile.endswith('"'):
|
elif idfile.startswith('"') and idfile.endswith('"'):
|
||||||
idfile = idfile[1:-1]
|
idfile = idfile[1:-1]
|
||||||
return {'hostname': sshconfig['hostname'],
|
return {
|
||||||
'port': int(sshconfig['port']),
|
'hostname': sshconfig['hostname'],
|
||||||
'user': sshconfig['user'],
|
'port': int(sshconfig['port']),
|
||||||
'idfile': idfile}
|
'user': sshconfig['user'],
|
||||||
|
'idfile': idfile,
|
||||||
|
}
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise FDroidBuildVmException("Error getting ssh config") from e
|
raise FDroidBuildVmException("Error getting ssh config") from e
|
||||||
|
|
||||||
@ -411,21 +443,27 @@ class LibvirtBuildVm(FDroidBuildVm):
|
|||||||
# TODO use a libvirt storage pool to ensure the img file is readable
|
# TODO use a libvirt storage pool to ensure the img file is readable
|
||||||
if not os.access(imagepath, os.R_OK):
|
if not os.access(imagepath, os.R_OK):
|
||||||
logging.warning(_('Cannot read "{path}"!').format(path=imagepath))
|
logging.warning(_('Cannot read "{path}"!').format(path=imagepath))
|
||||||
_check_call(['sudo', '/bin/chmod', '-R', 'a+rX', '/var/lib/libvirt/images'])
|
_check_call(
|
||||||
|
['sudo', '/bin/chmod', '-R', 'a+rX', '/var/lib/libvirt/images']
|
||||||
|
)
|
||||||
shutil.copy2(imagepath, 'box.img')
|
shutil.copy2(imagepath, 'box.img')
|
||||||
_check_call(['qemu-img', 'rebase', '-p', '-b', '', 'box.img'])
|
_check_call(['qemu-img', 'rebase', '-p', '-b', '', 'box.img'])
|
||||||
img_info_raw = _check_output(['qemu-img', 'info', '--output=json', 'box.img'])
|
img_info_raw = _check_output(
|
||||||
|
['qemu-img', 'info', '--output=json', 'box.img']
|
||||||
|
)
|
||||||
img_info = json.loads(img_info_raw.decode('utf-8'))
|
img_info = json.loads(img_info_raw.decode('utf-8'))
|
||||||
metadata = {"provider": "libvirt",
|
metadata = {
|
||||||
"format": img_info['format'],
|
"provider": "libvirt",
|
||||||
"virtual_size": math.ceil(img_info['virtual-size'] / (1024. ** 3)),
|
"format": img_info['format'],
|
||||||
}
|
"virtual_size": math.ceil(img_info['virtual-size'] / (1024.0 ** 3)),
|
||||||
|
}
|
||||||
|
|
||||||
logging.debug('preparing metadata.json for box %s', output)
|
logging.debug('preparing metadata.json for box %s', output)
|
||||||
with open('metadata.json', 'w') as fp:
|
with open('metadata.json', 'w') as fp:
|
||||||
fp.write(json.dumps(metadata))
|
fp.write(json.dumps(metadata))
|
||||||
logging.debug('preparing Vagrantfile for box %s', output)
|
logging.debug('preparing Vagrantfile for box %s', output)
|
||||||
vagrantfile = textwrap.dedent("""\
|
vagrantfile = textwrap.dedent(
|
||||||
|
"""\
|
||||||
Vagrant.configure("2") do |config|
|
Vagrant.configure("2") do |config|
|
||||||
config.ssh.username = "vagrant"
|
config.ssh.username = "vagrant"
|
||||||
config.ssh.password = "vagrant"
|
config.ssh.password = "vagrant"
|
||||||
@ -440,11 +478,18 @@ class LibvirtBuildVm(FDroidBuildVm):
|
|||||||
libvirt.memory = {memory}
|
libvirt.memory = {memory}
|
||||||
|
|
||||||
end
|
end
|
||||||
end""".format_map({'memory': str(int(domainInfo[1] / 1024)), 'cpus': str(domainInfo[3])}))
|
end""".format_map(
|
||||||
|
{
|
||||||
|
'memory': str(int(domainInfo[1] / 1024)),
|
||||||
|
'cpus': str(domainInfo[3]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
with open('Vagrantfile', 'w') as fp:
|
with open('Vagrantfile', 'w') as fp:
|
||||||
fp.write(vagrantfile)
|
fp.write(vagrantfile)
|
||||||
try:
|
try:
|
||||||
import libarchive
|
import libarchive
|
||||||
|
|
||||||
with libarchive.file_writer(output, 'gnutar', 'gzip') as tar:
|
with libarchive.file_writer(output, 'gnutar', 'gzip') as tar:
|
||||||
logging.debug('adding files to box %s ...', output)
|
logging.debug('adding files to box %s ...', output)
|
||||||
tar.add_files('metadata.json', 'Vagrantfile', 'box.img')
|
tar.add_files('metadata.json', 'Vagrantfile', 'box.img')
|
||||||
@ -506,6 +551,7 @@ class LibvirtBuildVm(FDroidBuildVm):
|
|||||||
|
|
||||||
def snapshot_exists(self, snapshot_name):
|
def snapshot_exists(self, snapshot_name):
|
||||||
import libvirt
|
import libvirt
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dom = self.conn.lookupByName(self.srvname)
|
dom = self.conn.lookupByName(self.srvname)
|
||||||
return dom.snapshotLookupByName(snapshot_name) is not None
|
return dom.snapshotLookupByName(snapshot_name) is not None
|
||||||
@ -515,6 +561,7 @@ class LibvirtBuildVm(FDroidBuildVm):
|
|||||||
def snapshot_revert(self, snapshot_name):
|
def snapshot_revert(self, snapshot_name):
|
||||||
logging.info("reverting vm '%s' to snapshot '%s'", self.srvname, snapshot_name)
|
logging.info("reverting vm '%s' to snapshot '%s'", self.srvname, snapshot_name)
|
||||||
import libvirt
|
import libvirt
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dom = self.conn.lookupByName(self.srvname)
|
dom = self.conn.lookupByName(self.srvname)
|
||||||
snap = dom.snapshotLookupByName(snapshot_name)
|
snap = dom.snapshotLookupByName(snapshot_name)
|
||||||
|
Loading…
Reference in New Issue
Block a user