1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-04 22:40:12 +01:00

Merge branch 'support-multiple-serverwebroots-and-fixes' into 'master'

Support multiple serverwebroots and fixes

This adds support for lists of serverwebroots in config.py, and cleans newlines and spaces in the repo_description and archive_description.
This commit is contained in:
Ciaran Gultnieks 2014-07-16 21:07:10 +00:00
commit 40945aded7
4 changed files with 132 additions and 39 deletions

View File

@ -33,11 +33,12 @@ repo_maxage = 0
repo_url = "https://MyFirstFDroidRepo.org/fdroid/repo" repo_url = "https://MyFirstFDroidRepo.org/fdroid/repo"
repo_name = "My First FDroid Repo Demo" repo_name = "My First FDroid Repo Demo"
repo_icon = "fdroid-icon.png" repo_icon = "fdroid-icon.png"
repo_description = ( repo_description = """
"This is a repository of apps to be used with FDroid. Applications in this " This is a repository of apps to be used with FDroid. Applications in this
+ "repository are either official binaries built by the original application " repository are either official binaries built by the original application
+ "developers, or are binaries built from source by the admin of f-droid.org " developers, or are binaries built from source by the admin of f-droid.org
+ "using the tools on https://gitlab.com/u/fdroid.") using the tools on https://gitlab.com/u/fdroid.
"""
# As above, but for the archive repo. # As above, but for the archive repo.
# archive_older sets the number of versions kept in the main repo, with all # archive_older sets the number of versions kept in the main repo, with all
@ -47,9 +48,9 @@ archive_older = 3
archive_url = "https://f-droid.org/archive" archive_url = "https://f-droid.org/archive"
archive_name = "My First FDroid Archive Demo" archive_name = "My First FDroid Archive Demo"
archive_icon = "fdroid-icon.png" archive_icon = "fdroid-icon.png"
archive_description = ( archive_description = """
"The repository of older versions of applications from the main demo " The repository of older versions of applications from the main demo repository.
+ "repository.") """
# The ID of a GPG key for making detached signatures for apks. Optional. # The ID of a GPG key for making detached signatures for apks. Optional.
@ -101,9 +102,15 @@ keyaliases['com.example.another.plugin'] = '@com.example.another'
# rsync/ssh format for a remote host/path. This is used for syncing a locally # rsync/ssh format for a remote host/path. This is used for syncing a locally
# generated repo to the server that is it hosted on. It must end in the # generated repo to the server that is it hosted on. It must end in the
# standard public repo name of "/fdroid", but can be in up to three levels of # standard public repo name of "/fdroid", but can be in up to three levels of
# sub-directories (i.e. /var/www/packagerepos/fdroid). # sub-directories (i.e. /var/www/packagerepos/fdroid). You can include
# multiple servers to sync to by wrapping the whole thing in {} or [], and
# including the serverwebroot strings in a comma-separated list.
# #
# serverwebroot = 'user@example:/var/www/fdroid' # serverwebroot = 'user@example:/var/www/fdroid'
# serverwebroot = {
# 'foo.com:/usr/share/nginx/www/fdroid',
# 'bar.info:/var/www/fdroid',
# }
# optionally specific which identity file to use when using rsync over SSH # optionally specific which identity file to use when using rsync over SSH

View File

@ -62,11 +62,12 @@ def get_default_config():
'repo_url': "https://MyFirstFDroidRepo.org/fdroid/repo", 'repo_url': "https://MyFirstFDroidRepo.org/fdroid/repo",
'repo_name': "My First FDroid Repo Demo", 'repo_name': "My First FDroid Repo Demo",
'repo_icon': "fdroid-icon.png", 'repo_icon': "fdroid-icon.png",
'repo_description': ( 'repo_description': '''
"This is a repository of apps to be used with FDroid. Applications in this " This is a repository of apps to be used with FDroid. Applications in this
+ "repository are either official binaries built by the original application " repository are either official binaries built by the original application
+ "developers, or are binaries built from source by the admin of f-droid.org " developers, or are binaries built from source by the admin of f-droid.org
+ "using the tools on https://gitlab.com/u/fdroid."), using the tools on https://gitlab.com/u/fdroid.
''',
'archive_older': 0, 'archive_older': 0,
} }
@ -163,12 +164,25 @@ def read_config(opts, config_file='config.py'):
if k in config: if k in config:
write_password_file(k) write_password_file(k)
# since this is used with rsync, where trailing slashes have meaning, for k in ["repo_description", "archive_description"]:
# ensure there is always a trailing slash if k in config:
config[k] = clean_description(config[k])
if 'serverwebroot' in config: if 'serverwebroot' in config:
if config['serverwebroot'][-1] != '/': if isinstance(config['serverwebroot'], basestring):
config['serverwebroot'] += '/' roots = [config['serverwebroot']]
config['serverwebroot'] = config['serverwebroot'].replace('//', '/') elif all(isinstance(item, basestring) for item in config['serverwebroot']):
roots = config['serverwebroot']
else:
raise TypeError('only accepts strings, lists, and tuples')
rootlist = []
for rootstr in roots:
# since this is used with rsync, where trailing slashes have
# meaning, ensure there is always a trailing slash
if rootstr[-1] != '/':
rootstr += '/'
rootlist.append(rootstr.replace('//', '/'))
config['serverwebroot'] = rootlist
return config return config
@ -290,6 +304,19 @@ def has_extension(filename, extension):
apk_regex = None apk_regex = None
def clean_description(description):
'Remove unneeded newlines and spaces from a block of description text'
returnstring = ''
# this is split up by paragraph to make removing the newlines easier
for paragraph in re.split(r'\n\n', description):
paragraph = re.sub('\r', '', paragraph)
paragraph = re.sub('\n', ' ', paragraph)
paragraph = re.sub(' {2,}', ' ', paragraph)
paragraph = re.sub('^\s*(\w)', r'\1', paragraph)
returnstring += paragraph + '\n\n'
return returnstring.rstrip('\n')
def apknameinfo(filename): def apknameinfo(filename):
global apk_regex global apk_regex
filename = os.path.basename(filename) filename = os.path.basename(filename)

View File

@ -20,6 +20,8 @@
import sys import sys
import hashlib import hashlib
import os import os
import paramiko
import pwd
import subprocess import subprocess
from optparse import OptionParser from optparse import OptionParser
import logging import logging
@ -114,7 +116,7 @@ def update_awsbucket(repo_section):
logging.info(' skipping ' + s3url) logging.info(' skipping ' + s3url)
def update_serverwebroot(repo_section): def update_serverwebroot(serverwebroot, repo_section):
rsyncargs = ['rsync', '--archive', '--delete'] rsyncargs = ['rsync', '--archive', '--delete']
if options.verbose: if options.verbose:
rsyncargs += ['--verbose'] rsyncargs += ['--verbose']
@ -129,11 +131,11 @@ def update_serverwebroot(repo_section):
# serverwebroot is guaranteed to have a trailing slash in common.py # serverwebroot is guaranteed to have a trailing slash in common.py
if subprocess.call(rsyncargs + if subprocess.call(rsyncargs +
['--exclude', indexxml, '--exclude', indexjar, ['--exclude', indexxml, '--exclude', indexjar,
repo_section, config['serverwebroot']]) != 0: repo_section, serverwebroot]) != 0:
sys.exit(1) sys.exit(1)
# use stricter checking on the indexes since they provide the signature # use stricter checking on the indexes since they provide the signature
rsyncargs += ['--checksum'] rsyncargs += ['--checksum']
sectionpath = config['serverwebroot'] + repo_section sectionpath = serverwebroot + repo_section
if subprocess.call(rsyncargs + [indexxml, sectionpath]) != 0: if subprocess.call(rsyncargs + [indexxml, sectionpath]) != 0:
sys.exit(1) sys.exit(1)
if subprocess.call(rsyncargs + [indexjar, sectionpath]) != 0: if subprocess.call(rsyncargs + [indexjar, sectionpath]) != 0:
@ -141,7 +143,8 @@ def update_serverwebroot(repo_section):
def _local_sync(fromdir, todir): def _local_sync(fromdir, todir):
rsyncargs = ['rsync', '--archive', '--one-file-system', '--delete'] rsyncargs = ['rsync', '--recursive', '--links', '--times',
'--one-file-system', '--delete', '--chmod=Da+rx,Fa-x,a+r,u+w']
# use stricter rsync checking on all files since people using offline mode # use stricter rsync checking on all files since people using offline mode
# are already prioritizing security above ease and speed # are already prioritizing security above ease and speed
rsyncargs += ['--checksum'] rsyncargs += ['--checksum']
@ -199,12 +202,11 @@ def main():
else: else:
standardwebroot = True standardwebroot = True
if config.get('serverwebroot'): for serverwebroot in config.get('serverwebroot', []):
serverwebroot = config['serverwebroot']
host, fdroiddir = serverwebroot.rstrip('/').split(':') host, fdroiddir = serverwebroot.rstrip('/').split(':')
repobase = os.path.basename(fdroiddir) repobase = os.path.basename(fdroiddir)
if standardwebroot and repobase != 'fdroid': if standardwebroot and repobase != 'fdroid':
logging.error('serverwebroot does not end with "fdroid", ' logging.error('serverwebroot path does not end with "fdroid", '
+ 'perhaps you meant one of these:\n\t' + 'perhaps you meant one of these:\n\t'
+ serverwebroot.rstrip('/') + '/fdroid\n\t' + serverwebroot.rstrip('/') + '/fdroid\n\t'
+ serverwebroot.rstrip('/').rstrip(repobase) + 'fdroid') + serverwebroot.rstrip('/').rstrip(repobase) + 'fdroid')
@ -254,18 +256,27 @@ def main():
os.mkdir('archive') os.mkdir('archive')
if args[0] == 'init': if args[0] == 'init':
if config.get('serverwebroot'): ssh = paramiko.SSHClient()
sshargs = ['ssh'] ssh.load_system_host_keys()
if options.quiet: for serverwebroot in config.get('serverwebroot', []):
sshargs += ['-q'] sshstr, remotepath = serverwebroot.rstrip('/').split(':')
if sshstr.find('@') >= 0:
username, hostname = sshstr.split('@')
else:
username = pwd.getpwuid(os.getuid())[0] # get effective uid
hostname = sshstr
ssh.connect(hostname, username=username)
sftp = ssh.open_sftp()
if os.path.basename(remotepath) \
not in sftp.listdir(os.path.dirname(remotepath)):
sftp.mkdir(remotepath, mode=0755)
for repo_section in repo_sections: for repo_section in repo_sections:
cmd = sshargs + [host, 'mkdir -p', fdroiddir + '/' + repo_section] repo_path = os.path.join(remotepath, repo_section)
if options.verbose: if os.path.basename(repo_path) \
# ssh -v produces different output than rsync -v, so this not in sftp.listdir(remotepath):
# simulates rsync -v sftp.mkdir(repo_path, mode=0755)
logging.info(' '.join(cmd)) sftp.close()
if subprocess.call(cmd) != 0: ssh.close()
sys.exit(1)
elif args[0] == 'update': elif args[0] == 'update':
for repo_section in repo_sections: for repo_section in repo_sections:
if local_copy_dir is not None: if local_copy_dir is not None:
@ -273,8 +284,8 @@ def main():
sync_from_localcopy(repo_section, local_copy_dir) sync_from_localcopy(repo_section, local_copy_dir)
else: else:
update_localcopy(repo_section, local_copy_dir) update_localcopy(repo_section, local_copy_dir)
if config.get('serverwebroot'): for serverwebroot in config.get('serverwebroot', []):
update_serverwebroot(repo_section) update_serverwebroot(serverwebroot, repo_section)
if config.get('awsbucket'): if config.get('awsbucket'):
update_awsbucket(repo_section) update_awsbucket(repo_section)

48
tests/description-parsing.py Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import os
import sys
sys.path.insert(1, os.path.join(os.getcwd(), '..', 'fdroidserver'))
import common
config = common.get_default_config()
testtext = '''
This is a block of text that has been wrapped to fit nicely in PEP8 style:
GnuPrivacyGuard extends the gpgcli command line tool to bring an integrated
privacy engine to your Android. It gives you command line access to the entire
GnuPG suite of encryption software. It also serves as the test bed for
complete Android integration for all of GnuPG's crypto services, including
OpenPGP, symmetric encryption, and more.
GPG is GNUs tool for end-to-end secure communication and encrypted data
storage. This trusted protocol is the free software alternative to PGP. This
app is built upon GnuPG 2.1, the new modularized version of GnuPG that now
supports S/MIME.
GPG aims to provide an integrated experience, so clicking on PGP files should
"just work". You can also share files to GPG to encrypt them. GPG will also
respond when you click on a PGP fingerprint URL (one that starts with
openpgp4fpr:).
Before using GPG, be sure to launch the app and let it finish its installation
process. Once it has completed, then you're ready to use it. The easiest way
to get started with GPG is to install [[jackpal.androidterm]]. GPG will
automatically configure Android Terminal Emulator as long as you have the
"Allow PATH extensions" settings enabled.
'''
archive_description = """
The repository of older versions of applications from the main demo repository.
"""
print('\n\n\n----------------------------------------------------')
print(common.clean_description(testtext))
print('\n\n\n----------------------------------------------------')
print(common.clean_description(archive_description))
print('\n\n\n----------------------------------------------------')
print(common.clean_description(config['repo_description']))