mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-07-05 08:50:08 +02:00
Create archive repo (configurable option)
This commit is contained in:
parent
5ab225e1e1
commit
48296df5b0
@ -21,6 +21,20 @@ are binaries built from source by the admin of f-droid.org using the tools on
|
|||||||
https://gitorious.org/f-droid.
|
https://gitorious.org/f-droid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# As above, but for the archive repo.
|
||||||
|
# archive_older sets the number of versions kept in the main repo, with all
|
||||||
|
# older ones going to the archive. Set it to 0, and there will be no archive
|
||||||
|
# repository, and no need to define the other archive_ values.
|
||||||
|
archive_older = 3
|
||||||
|
archive_url = "https://f-droid.org/archive"
|
||||||
|
archive_name = "F-Droid Archive"
|
||||||
|
archive_icon = "fdroid-icon.png"
|
||||||
|
archive_description = """
|
||||||
|
The archive repository of the F-Droid client. This contains older versions
|
||||||
|
of applications from the main repository.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
#The key (from the keystore defined below) to be used for signing the
|
#The key (from the keystore defined below) to be used for signing the
|
||||||
#repository itself. Can be None for an unsigned repository.
|
#repository itself. Can be None for an unsigned repository.
|
||||||
repo_keyalias = None
|
repo_keyalias = None
|
||||||
|
@ -22,10 +22,8 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import zipfile
|
|
||||||
import tarfile
|
import tarfile
|
||||||
import traceback
|
import traceback
|
||||||
from xml.dom.minidom import Document
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
import common
|
import common
|
||||||
@ -465,8 +463,8 @@ def build_local(app, thisbuild, vcs, build_dir, output_dir, extlib_dir, tmp_dir,
|
|||||||
os.path.join(output_dir, tarfilename))
|
os.path.join(output_dir, tarfilename))
|
||||||
|
|
||||||
|
|
||||||
def trybuild(app, thisbuild, build_dir, output_dir, extlib_dir, tmp_dir,
|
def trybuild(app, thisbuild, build_dir, output_dir, also_check_dir, extlib_dir,
|
||||||
repo_dir, vcs, test, server, install, force, verbose=False):
|
tmp_dir, repo_dir, vcs, test, server, install, force, verbose=False):
|
||||||
"""
|
"""
|
||||||
Build a particular version of an application, if it needs building.
|
Build a particular version of an application, if it needs building.
|
||||||
|
|
||||||
@ -481,6 +479,12 @@ def trybuild(app, thisbuild, build_dir, output_dir, extlib_dir, tmp_dir,
|
|||||||
if os.path.exists(dest) or (not test and os.path.exists(dest_repo)):
|
if os.path.exists(dest) or (not test and os.path.exists(dest_repo)):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if also_check_dir:
|
||||||
|
dest_also = os.path.join(also_check_dir, app['id'] + '_' +
|
||||||
|
thisbuild['vercode'] + '.apk')
|
||||||
|
if os.path.exists(dest_also):
|
||||||
|
return False
|
||||||
|
|
||||||
if thisbuild['commit'].startswith('!'):
|
if thisbuild['commit'].startswith('!'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -554,10 +558,13 @@ options = None
|
|||||||
def main():
|
def main():
|
||||||
|
|
||||||
global options
|
global options
|
||||||
|
|
||||||
# Read configuration...
|
# Read configuration...
|
||||||
globals()['build_server_always'] = False
|
globals()['build_server_always'] = False
|
||||||
globals()['mvn3'] = "mvn3"
|
globals()['mvn3'] = "mvn3"
|
||||||
|
globals()['archive_older'] = 0
|
||||||
execfile('config.py', globals())
|
execfile('config.py', globals())
|
||||||
|
|
||||||
options, args = parse_commandline()
|
options, args = parse_commandline()
|
||||||
if build_server_always:
|
if build_server_always:
|
||||||
options.server = True
|
options.server = True
|
||||||
@ -586,6 +593,11 @@ def main():
|
|||||||
print "Creating output directory"
|
print "Creating output directory"
|
||||||
os.makedirs(output_dir)
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
if archive_older != 0:
|
||||||
|
also_check_dir = 'archive'
|
||||||
|
else:
|
||||||
|
also_check_dir = None
|
||||||
|
|
||||||
repo_dir = 'repo'
|
repo_dir = 'repo'
|
||||||
|
|
||||||
build_dir = 'build'
|
build_dir = 'build'
|
||||||
@ -628,9 +640,10 @@ def main():
|
|||||||
for thisbuild in app['builds']:
|
for thisbuild in app['builds']:
|
||||||
wikilog = None
|
wikilog = None
|
||||||
try:
|
try:
|
||||||
if trybuild(app, thisbuild, build_dir, output_dir, extlib_dir,
|
if trybuild(app, thisbuild, build_dir, output_dir, also_check_dir,
|
||||||
tmp_dir, repo_dir, vcs, options.test, options.server,
|
extlib_dir, tmp_dir, repo_dir, vcs, options.test,
|
||||||
options.install, options.force, options.verbose):
|
options.server, options.install, options.force,
|
||||||
|
options.verbose):
|
||||||
build_succeeded.append(app)
|
build_succeeded.append(app)
|
||||||
wikilog = "Build succeeded"
|
wikilog = "Build succeeded"
|
||||||
except BuildException as be:
|
except BuildException as be:
|
||||||
|
@ -25,6 +25,8 @@ from optparse import OptionParser
|
|||||||
def main():
|
def main():
|
||||||
|
|
||||||
#Read configuration...
|
#Read configuration...
|
||||||
|
global archive_older
|
||||||
|
archive_older = 0
|
||||||
execfile('config.py', globals())
|
execfile('config.py', globals())
|
||||||
|
|
||||||
# Parse command line...
|
# Parse command line...
|
||||||
@ -41,12 +43,22 @@ def main():
|
|||||||
print "The only command currently supported is 'update'"
|
print "The only command currently supported is 'update'"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if subprocess.call(['rsync', '-u', '-v', '-r', '--delete', '--exclude', 'repo/index.xml', '--exclude', 'repo/index.jar', 'repo', serverwebroot]) != 0:
|
repodirs = ['repo']
|
||||||
sys.exit(1)
|
if archive_older != 0:
|
||||||
if subprocess.call(['rsync', '-u', '-v', '-r', '--delete', 'repo/index.xml', serverwebroot + '/repo']) != 0:
|
repodirs.append('archive')
|
||||||
sys.exit(1)
|
|
||||||
if subprocess.call(['rsync', '-u', '-v', '-r', '--delete', 'repo/index.jar', serverwebroot + '/repo']) != 0:
|
for repodir in repodirs:
|
||||||
sys.exit(1)
|
index = os.path.join(repodir, 'index.xml')
|
||||||
|
indexjar = os.path.join(repodir, 'index.jar')
|
||||||
|
if subprocess.call(['rsync', '-u', '-v', '-r', '--delete',
|
||||||
|
'--exclude', index, '--exclude', indexjar, repodir, serverwebroot]) != 0:
|
||||||
|
sys.exit(1)
|
||||||
|
if subprocess.call(['rsync', '-u', '-v', '-r', '--delete',
|
||||||
|
index, serverwebroot + '/' + repodir]) != 0:
|
||||||
|
sys.exit(1)
|
||||||
|
if subprocess.call(['rsync', '-u', '-v', '-r', '--delete',
|
||||||
|
indexjar, serverwebroot + '/' + repodir]) != 0:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# update.py - part of the FDroid server tools
|
# update.py - part of the FDroid server tools
|
||||||
# Copyright (C) 2010-12, Ciaran Gultnieks, ciaran@ciarang.com
|
# Copyright (C) 2010-2013, Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -182,88 +182,52 @@ def update_wiki(apps, apks, verbose=False):
|
|||||||
print "...FAILED to create page"
|
print "...FAILED to create page"
|
||||||
|
|
||||||
|
|
||||||
|
def delete_disabled_builds(apps, apkcache, repodirs):
|
||||||
|
"""Delete disabled build outputs.
|
||||||
|
|
||||||
def main():
|
:param apps: list of all applications, as per common.read_metadata
|
||||||
|
:param apkcache: current apk cache information
|
||||||
# Read configuration...
|
:param repodirs: the repo directories to process
|
||||||
global update_stats
|
"""
|
||||||
update_stats = False
|
for app in apps:
|
||||||
execfile('config.py', globals())
|
for build in app['builds']:
|
||||||
|
if build['commit'].startswith('!'):
|
||||||
# Parse command line...
|
apkfilename = app['id'] + '_' + str(build['vercode']) + '.apk'
|
||||||
parser = OptionParser()
|
for repodir in repodirs:
|
||||||
parser.add_option("-c", "--createmeta", action="store_true", default=False,
|
apkpath = os.path.join(repodir, apkfilename)
|
||||||
help="Create skeleton metadata files that are missing")
|
srcpath = os.path.join(repodir, apkfilename[:-4] + "_src.tar.gz")
|
||||||
parser.add_option("-v", "--verbose", action="store_true", default=False,
|
for name in [apkpath, srcpath]:
|
||||||
help="Spew out even more information than normal")
|
if os.path.exists(name):
|
||||||
parser.add_option("-q", "--quiet", action="store_true", default=False,
|
print "Deleting disabled build output " + apkfilename
|
||||||
help="No output, except for warnings and errors")
|
os.remove(name)
|
||||||
parser.add_option("-b", "--buildreport", action="store_true", default=False,
|
if apkfilename in apkcache:
|
||||||
help="Report on build data status")
|
del apkcache[apkfilename]
|
||||||
parser.add_option("-i", "--interactive", default=False, action="store_true",
|
|
||||||
help="Interactively ask about things that need updating.")
|
|
||||||
parser.add_option("-e", "--editor", default="/etc/alternatives/editor",
|
|
||||||
help="Specify editor to use in interactive mode. Default "+
|
|
||||||
"is /etc/alternatives/editor")
|
|
||||||
parser.add_option("-w", "--wiki", default=False, action="store_true",
|
|
||||||
help="Update the wiki")
|
|
||||||
parser.add_option("", "--pretty", action="store_true", default=False,
|
|
||||||
help="Produce human-readable index.xml")
|
|
||||||
parser.add_option("--clean", action="store_true", default=False,
|
|
||||||
help="Clean update - don't uses caches, reprocess all apks")
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
icon_dir=os.path.join('repo','icons')
|
def scan_apks(apps, apkcache, repodir, knownapks):
|
||||||
|
"""Scan the apks in the given repo directory.
|
||||||
|
|
||||||
|
This also extracts the icons.
|
||||||
|
|
||||||
|
:param apps: list of all applications, as per common.read_metadata
|
||||||
|
:param apkcache: current apk cache information
|
||||||
|
:param repodir: repo directory to scan
|
||||||
|
:param knownapks: known apks info
|
||||||
|
:returns: (apks, cachechanged) where apks is a list of apk information,
|
||||||
|
and cachechanged is True if the apkcache got changed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cachechanged = False
|
||||||
|
|
||||||
|
icon_dir = os.path.join(repodir ,'icons')
|
||||||
# Delete and re-create the icon directory...
|
# Delete and re-create the icon directory...
|
||||||
if options.clean and os.path.exists(icon_dir):
|
if options.clean and os.path.exists(icon_dir):
|
||||||
shutil.rmtree(icon_dir)
|
shutil.rmtree(icon_dir)
|
||||||
if not os.path.exists(icon_dir):
|
if not os.path.exists(icon_dir):
|
||||||
os.makedirs(icon_dir)
|
os.makedirs(icon_dir)
|
||||||
|
|
||||||
warnings = 0
|
|
||||||
|
|
||||||
# Get all apps...
|
|
||||||
apps = common.read_metadata(verbose=options.verbose)
|
|
||||||
|
|
||||||
# Generate a list of categories...
|
|
||||||
categories = []
|
|
||||||
for app in apps:
|
|
||||||
cats = app['Category'].split(';')
|
|
||||||
for cat in cats:
|
|
||||||
if cat not in categories:
|
|
||||||
categories.append(cat)
|
|
||||||
|
|
||||||
# Read known apks data (will be updated and written back when we've finished)
|
|
||||||
knownapks = common.KnownApks()
|
|
||||||
|
|
||||||
# Gather information about all the apk files in the repo directory, using
|
|
||||||
# cached data if possible.
|
|
||||||
apkcachefile = os.path.join('tmp', 'apkcache')
|
|
||||||
if not options.clean and os.path.exists(apkcachefile):
|
|
||||||
with open(apkcachefile, 'rb') as cf:
|
|
||||||
apkcache = pickle.load(cf)
|
|
||||||
else:
|
|
||||||
apkcache = {}
|
|
||||||
cachechanged = False
|
|
||||||
|
|
||||||
# Check repo directory for disabled builds and remove them...
|
|
||||||
for app in apps:
|
|
||||||
for build in app['builds']:
|
|
||||||
if build['commit'].startswith('!'):
|
|
||||||
apkfilename = app['id'] + '_' + str(build['vercode']) + '.apk'
|
|
||||||
apkpath = os.path.join('repo', apkfilename)
|
|
||||||
srcpath = apkfilename[:-4] + "_src.tar.gz"
|
|
||||||
for name in [apkpath, srcpath]:
|
|
||||||
if os.path.exists(name):
|
|
||||||
print "Deleting disabled build output " + apkfilename
|
|
||||||
os.remove(name)
|
|
||||||
if apkfilename in apkcache:
|
|
||||||
del apkcache[apkfilename]
|
|
||||||
|
|
||||||
apks = []
|
apks = []
|
||||||
for apkfile in glob.glob(os.path.join('repo','*.apk')):
|
for apkfile in glob.glob(os.path.join(repodir, '*.apk')):
|
||||||
|
|
||||||
apkfilename = apkfile[5:]
|
apkfilename = apkfile[5:]
|
||||||
if apkfilename.find(' ') != -1:
|
if apkfilename.find(' ') != -1:
|
||||||
@ -282,7 +246,7 @@ def main():
|
|||||||
print "Processing " + apkfilename
|
print "Processing " + apkfilename
|
||||||
thisinfo = {}
|
thisinfo = {}
|
||||||
thisinfo['apkname'] = apkfilename
|
thisinfo['apkname'] = apkfilename
|
||||||
if os.path.exists(os.path.join('repo', srcfilename)):
|
if os.path.exists(os.path.join(repodir, srcfilename)):
|
||||||
thisinfo['srcname'] = srcfilename
|
thisinfo['srcname'] = srcfilename
|
||||||
thisinfo['size'] = os.path.getsize(apkfile)
|
thisinfo['size'] = os.path.getsize(apkfile)
|
||||||
thisinfo['permissions'] = []
|
thisinfo['permissions'] = []
|
||||||
@ -377,7 +341,6 @@ def main():
|
|||||||
iconfile.close()
|
iconfile.close()
|
||||||
except:
|
except:
|
||||||
print "WARNING: Error retrieving icon file"
|
print "WARNING: Error retrieving icon file"
|
||||||
warnings += 1
|
|
||||||
apk.close()
|
apk.close()
|
||||||
|
|
||||||
# Record in known apks, getting the added date at the same time..
|
# Record in known apks, getting the added date at the same time..
|
||||||
@ -390,79 +353,20 @@ def main():
|
|||||||
|
|
||||||
apks.append(thisinfo)
|
apks.append(thisinfo)
|
||||||
|
|
||||||
if cachechanged:
|
return apks, cachechanged
|
||||||
with open(apkcachefile, 'wb') as cf:
|
|
||||||
pickle.dump(apkcache, cf)
|
|
||||||
|
|
||||||
# Some information from the apks needs to be applied up to the application
|
|
||||||
# level. When doing this, we use the info from the most recent version's apk.
|
|
||||||
# We deal with figuring out when the app was added and last updated at the
|
|
||||||
# same time.
|
|
||||||
for app in apps:
|
|
||||||
bestver = 0
|
|
||||||
added = None
|
|
||||||
lastupdated = None
|
|
||||||
for apk in apks:
|
|
||||||
if apk['id'] == app['id']:
|
|
||||||
if apk['versioncode'] > bestver:
|
|
||||||
bestver = apk['versioncode']
|
|
||||||
bestapk = apk
|
|
||||||
|
|
||||||
if 'added' in apk:
|
def make_index(apps, apks, repodir, archive, categories):
|
||||||
if not added or apk['added'] < added:
|
"""Make a repo index.
|
||||||
added = apk['added']
|
|
||||||
if not lastupdated or apk['added'] > lastupdated:
|
|
||||||
lastupdated = apk['added']
|
|
||||||
|
|
||||||
if added:
|
:param apps: fully populated apps list
|
||||||
app['added'] = added
|
:param apks: full populated apks list
|
||||||
else:
|
:param repodir: the repo directory
|
||||||
print "WARNING: Don't know when " + app['id'] + " was added"
|
:param archive: True if this is the archive repo, False if it's the
|
||||||
if lastupdated:
|
main one.
|
||||||
app['lastupdated'] = lastupdated
|
:param categories: list of categories
|
||||||
else:
|
"""
|
||||||
print "WARNING: Don't know when " + app['id'] + " was last updated"
|
|
||||||
|
|
||||||
if bestver == 0:
|
|
||||||
if app['Name'] is None:
|
|
||||||
app['Name'] = app['id']
|
|
||||||
app['icon'] = ''
|
|
||||||
if app['Disabled'] is None:
|
|
||||||
print "WARNING: Application " + app['id'] + " has no packages"
|
|
||||||
else:
|
|
||||||
if app['Name'] is None:
|
|
||||||
app['Name'] = bestapk['name']
|
|
||||||
app['icon'] = bestapk['icon']
|
|
||||||
|
|
||||||
# Generate warnings for apk's with no metadata (or create skeleton
|
|
||||||
# metadata files, if requested on the command line)
|
|
||||||
for apk in apks:
|
|
||||||
found = False
|
|
||||||
for app in apps:
|
|
||||||
if app['id'] == apk['id']:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
if options.createmeta:
|
|
||||||
f = open(os.path.join('metadata', apk['id'] + '.txt'), 'w')
|
|
||||||
f.write("License:Unknown\n")
|
|
||||||
f.write("Web Site:\n")
|
|
||||||
f.write("Source Code:\n")
|
|
||||||
f.write("Issue Tracker:\n")
|
|
||||||
f.write("Summary:" + apk['name'] + "\n")
|
|
||||||
f.write("Description:\n")
|
|
||||||
f.write(apk['name'] + "\n")
|
|
||||||
f.write(".\n")
|
|
||||||
f.close()
|
|
||||||
print "Generated skeleton metadata for " + apk['id']
|
|
||||||
else:
|
|
||||||
print "WARNING: " + apk['apkname'] + " (" + apk['id'] + ") has no metadata"
|
|
||||||
print " " + apk['name'] + " - " + apk['version']
|
|
||||||
|
|
||||||
#Sort the app list by name, then the web site doesn't have to by default:
|
|
||||||
apps = sorted(apps, key=lambda app: app['Name'].upper())
|
|
||||||
|
|
||||||
# Create the index
|
|
||||||
doc = Document()
|
doc = Document()
|
||||||
|
|
||||||
def addElement(name, value, doc, parent):
|
def addElement(name, value, doc, parent):
|
||||||
@ -478,9 +382,16 @@ def main():
|
|||||||
doc.appendChild(root)
|
doc.appendChild(root)
|
||||||
|
|
||||||
repoel = doc.createElement("repo")
|
repoel = doc.createElement("repo")
|
||||||
repoel.setAttribute("name", repo_name)
|
if archive:
|
||||||
repoel.setAttribute("icon", os.path.basename(repo_icon))
|
repoel.setAttribute("name", archive_name)
|
||||||
repoel.setAttribute("url", repo_url)
|
repoel.setAttribute("icon", os.path.basename(archive_icon))
|
||||||
|
repoel.setAttribute("url", archive_url)
|
||||||
|
addElement('description', archive_description, doc, repoel)
|
||||||
|
else:
|
||||||
|
repoel.setAttribute("name", repo_name)
|
||||||
|
repoel.setAttribute("icon", os.path.basename(repo_icon))
|
||||||
|
repoel.setAttribute("url", repo_url)
|
||||||
|
addElement('description', repo_description, doc, repoel)
|
||||||
|
|
||||||
if repo_keyalias != None:
|
if repo_keyalias != None:
|
||||||
|
|
||||||
@ -509,30 +420,19 @@ def main():
|
|||||||
|
|
||||||
repoel.setAttribute("pubkey", extract_pubkey())
|
repoel.setAttribute("pubkey", extract_pubkey())
|
||||||
|
|
||||||
addElement('description', repo_description, doc, repoel)
|
|
||||||
root.appendChild(repoel)
|
root.appendChild(repoel)
|
||||||
|
|
||||||
apps_inrepo = 0
|
|
||||||
apps_disabled = 0
|
|
||||||
apps_nopkg = 0
|
|
||||||
|
|
||||||
for app in apps:
|
for app in apps:
|
||||||
|
|
||||||
if app['Disabled'] is None:
|
if app['Disabled'] is None:
|
||||||
|
|
||||||
# Get a list of the apks for this app...
|
# Get a list of the apks for this app...
|
||||||
gotcurrentver = False
|
|
||||||
apklist = []
|
apklist = []
|
||||||
for apk in apks:
|
for apk in apks:
|
||||||
if apk['id'] == app['id']:
|
if apk['id'] == app['id']:
|
||||||
if str(apk['versioncode']) == app['Current Version Code']:
|
|
||||||
gotcurrentver = True
|
|
||||||
apklist.append(apk)
|
apklist.append(apk)
|
||||||
|
|
||||||
if len(apklist) == 0:
|
if len(apklist) != 0:
|
||||||
apps_nopkg += 1
|
|
||||||
else:
|
|
||||||
apps_inrepo += 1
|
|
||||||
apel = doc.createElement("application")
|
apel = doc.createElement("application")
|
||||||
apel.setAttribute("id", app['id'])
|
apel.setAttribute("id", app['id'])
|
||||||
root.appendChild(apel)
|
root.appendChild(apel)
|
||||||
@ -627,62 +527,7 @@ def main():
|
|||||||
if len(features) > 0:
|
if len(features) > 0:
|
||||||
addElement('features', features, doc, apkel)
|
addElement('features', features, doc, apkel)
|
||||||
|
|
||||||
if options.buildreport:
|
of = open(os.path.join(repodir, 'index.xml'), 'wb')
|
||||||
if len(app['builds']) == 0:
|
|
||||||
print ("WARNING: No builds defined for " + app['id'] +
|
|
||||||
" Source: " + app['Source Code'])
|
|
||||||
warnings += 1
|
|
||||||
else:
|
|
||||||
if app['Current Version Code'] != '0':
|
|
||||||
gotbuild = False
|
|
||||||
for build in app['builds']:
|
|
||||||
if build['vercode'] == app['Current Version Code']:
|
|
||||||
gotbuild = True
|
|
||||||
if not gotbuild:
|
|
||||||
print ("WARNING: No build data for current version of "
|
|
||||||
+ app['id'] + " (" + app['Current Version']
|
|
||||||
+ ") " + app['Source Code'])
|
|
||||||
warnings += 1
|
|
||||||
|
|
||||||
# If we don't have the current version, check if there is a build
|
|
||||||
# with a commit ID starting with '!' - this means we can't build it
|
|
||||||
# for some reason, and don't want hassling about it...
|
|
||||||
if not gotcurrentver and app['Current Version Code'] != '0':
|
|
||||||
for build in app['builds']:
|
|
||||||
if build['vercode'] == app['Current Version Code']:
|
|
||||||
gotcurrentver = True
|
|
||||||
|
|
||||||
# Output a message of harassment if we don't have the current version:
|
|
||||||
if not gotcurrentver and app['Current Version Code'] != '0':
|
|
||||||
addr = app['Source Code']
|
|
||||||
print "WARNING: Don't have current version (" + app['Current Version'] + ") of " + app['Name']
|
|
||||||
print " (" + app['id'] + ") " + addr
|
|
||||||
warnings += 1
|
|
||||||
if options.verbose:
|
|
||||||
# A bit of extra debug info, basically for diagnosing
|
|
||||||
# app developer mistakes:
|
|
||||||
print " Current vercode:" + app['Current Version Code']
|
|
||||||
print " Got:"
|
|
||||||
for apk in apks:
|
|
||||||
if apk['id'] == app['id']:
|
|
||||||
print " " + str(apk['versioncode']) + " - " + apk['version']
|
|
||||||
if options.interactive:
|
|
||||||
print "Build data out of date for " + app['id']
|
|
||||||
while True:
|
|
||||||
answer = raw_input("[I]gnore, [E]dit or [Q]uit?").lower()
|
|
||||||
if answer == 'i':
|
|
||||||
break
|
|
||||||
elif answer == 'e':
|
|
||||||
subprocess.call([options.editor,
|
|
||||||
os.path.join('metadata',
|
|
||||||
app['id'] + '.txt')])
|
|
||||||
break
|
|
||||||
elif answer == 'q':
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
apps_disabled += 1
|
|
||||||
|
|
||||||
of = open(os.path.join('repo','index.xml'), 'wb')
|
|
||||||
if options.pretty:
|
if options.pretty:
|
||||||
output = doc.toprettyxml()
|
output = doc.toprettyxml()
|
||||||
else:
|
else:
|
||||||
@ -698,7 +543,7 @@ def main():
|
|||||||
|
|
||||||
#Create a jar of the index...
|
#Create a jar of the index...
|
||||||
p = subprocess.Popen(['jar', 'cf', 'index.jar', 'index.xml'],
|
p = subprocess.Popen(['jar', 'cf', 'index.jar', 'index.xml'],
|
||||||
cwd='repo', stdout=subprocess.PIPE)
|
cwd=repodir, stdout=subprocess.PIPE)
|
||||||
output = p.communicate()[0]
|
output = p.communicate()[0]
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
print output
|
print output
|
||||||
@ -710,7 +555,7 @@ def main():
|
|||||||
p = subprocess.Popen(['jarsigner', '-keystore', keystore,
|
p = subprocess.Popen(['jarsigner', '-keystore', keystore,
|
||||||
'-storepass', keystorepass, '-keypass', keypass,
|
'-storepass', keystorepass, '-keypass', keypass,
|
||||||
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
|
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
|
||||||
os.path.join('repo', 'index.jar') , repo_keyalias], stdout=subprocess.PIPE)
|
os.path.join(repodir, 'index.jar') , repo_keyalias], stdout=subprocess.PIPE)
|
||||||
output = p.communicate()[0]
|
output = p.communicate()[0]
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
print "Failed to sign index"
|
print "Failed to sign index"
|
||||||
@ -720,6 +565,7 @@ def main():
|
|||||||
print output
|
print output
|
||||||
|
|
||||||
# Copy the repo icon into the repo directory...
|
# Copy the repo icon into the repo directory...
|
||||||
|
icon_dir=os.path.join(repodir ,'icons')
|
||||||
iconfilename = os.path.join(icon_dir, os.path.basename(repo_icon))
|
iconfilename = os.path.join(icon_dir, os.path.basename(repo_icon))
|
||||||
shutil.copyfile(repo_icon, iconfilename)
|
shutil.copyfile(repo_icon, iconfilename)
|
||||||
|
|
||||||
@ -727,10 +573,185 @@ def main():
|
|||||||
catdata = ''
|
catdata = ''
|
||||||
for cat in categories:
|
for cat in categories:
|
||||||
catdata += cat + '\n'
|
catdata += cat + '\n'
|
||||||
f = open('repo/categories.txt', 'w')
|
f = open(os.path.join(repodir, 'categories.txt'), 'w')
|
||||||
f.write(catdata)
|
f.write(catdata)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def archive_old_apks(apps, apks, repodir, archivedir, keepversions):
|
||||||
|
|
||||||
|
for app in apps:
|
||||||
|
|
||||||
|
# Get a list of the apks for this app...
|
||||||
|
apklist = []
|
||||||
|
for apk in apks:
|
||||||
|
if apk['id'] == app['id']:
|
||||||
|
apklist.append(apk)
|
||||||
|
|
||||||
|
# Sort the apk list into version order...
|
||||||
|
apklist = sorted(apklist, key=lambda apk: apk['versioncode'], reverse=True)
|
||||||
|
|
||||||
|
if len(apklist) > keepversions:
|
||||||
|
for apk in apklist[keepversions:]:
|
||||||
|
print "Moving " + apk['apkname'] + " to archive"
|
||||||
|
shutil.move(os.path.join(repodir, apk['apkname']),
|
||||||
|
os.path.join(archivedir, apk['apkname']))
|
||||||
|
if 'srcname' in apk:
|
||||||
|
shutil.move(os.path.join(repodir, apk['srcname']),
|
||||||
|
os.path.join(archivedir, apk['srcname']))
|
||||||
|
apks.remove(apk)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
# Read configuration...
|
||||||
|
global update_stats, archive_older
|
||||||
|
update_stats = False
|
||||||
|
archive_older = 0
|
||||||
|
execfile('config.py', globals())
|
||||||
|
|
||||||
|
# Parse command line...
|
||||||
|
global options
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-c", "--createmeta", action="store_true", default=False,
|
||||||
|
help="Create skeleton metadata files that are missing")
|
||||||
|
parser.add_option("-v", "--verbose", action="store_true", default=False,
|
||||||
|
help="Spew out even more information than normal")
|
||||||
|
parser.add_option("-q", "--quiet", action="store_true", default=False,
|
||||||
|
help="No output, except for warnings and errors")
|
||||||
|
parser.add_option("-b", "--buildreport", action="store_true", default=False,
|
||||||
|
help="Report on build data status")
|
||||||
|
parser.add_option("-i", "--interactive", default=False, action="store_true",
|
||||||
|
help="Interactively ask about things that need updating.")
|
||||||
|
parser.add_option("-e", "--editor", default="/etc/alternatives/editor",
|
||||||
|
help="Specify editor to use in interactive mode. Default "+
|
||||||
|
"is /etc/alternatives/editor")
|
||||||
|
parser.add_option("-w", "--wiki", default=False, action="store_true",
|
||||||
|
help="Update the wiki")
|
||||||
|
parser.add_option("", "--pretty", action="store_true", default=False,
|
||||||
|
help="Produce human-readable index.xml")
|
||||||
|
parser.add_option("--clean", action="store_true", default=False,
|
||||||
|
help="Clean update - don't uses caches, reprocess all apks")
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
# Get all apps...
|
||||||
|
apps = common.read_metadata(verbose=options.verbose)
|
||||||
|
|
||||||
|
# Generate a list of categories...
|
||||||
|
categories = []
|
||||||
|
for app in apps:
|
||||||
|
cats = app['Category'].split(';')
|
||||||
|
for cat in cats:
|
||||||
|
if cat not in categories:
|
||||||
|
categories.append(cat)
|
||||||
|
|
||||||
|
# Read known apks data (will be updated and written back when we've finished)
|
||||||
|
knownapks = common.KnownApks()
|
||||||
|
|
||||||
|
# Gather information about all the apk files in the repo directory, using
|
||||||
|
# cached data if possible.
|
||||||
|
apkcachefile = os.path.join('tmp', 'apkcache')
|
||||||
|
if not options.clean and os.path.exists(apkcachefile):
|
||||||
|
with open(apkcachefile, 'rb') as cf:
|
||||||
|
apkcache = pickle.load(cf)
|
||||||
|
else:
|
||||||
|
apkcache = {}
|
||||||
|
cachechanged = False
|
||||||
|
|
||||||
|
repodirs = ['repo']
|
||||||
|
if archive_older != 0:
|
||||||
|
repodirs.append('archive')
|
||||||
|
if not os.path.exists('archive'):
|
||||||
|
os.mkdir('archive')
|
||||||
|
|
||||||
|
delete_disabled_builds(apps, apkcache, repodirs)
|
||||||
|
|
||||||
|
apks, cc = scan_apks(apps, apkcache, repodirs[0], knownapks)
|
||||||
|
if cc:
|
||||||
|
cachechanged = True
|
||||||
|
|
||||||
|
# Some information from the apks needs to be applied up to the application
|
||||||
|
# level. When doing this, we use the info from the most recent version's apk.
|
||||||
|
# We deal with figuring out when the app was added and last updated at the
|
||||||
|
# same time.
|
||||||
|
for app in apps:
|
||||||
|
bestver = 0
|
||||||
|
added = None
|
||||||
|
lastupdated = None
|
||||||
|
for apk in apks:
|
||||||
|
if apk['id'] == app['id']:
|
||||||
|
if apk['versioncode'] > bestver:
|
||||||
|
bestver = apk['versioncode']
|
||||||
|
bestapk = apk
|
||||||
|
|
||||||
|
if 'added' in apk:
|
||||||
|
if not added or apk['added'] < added:
|
||||||
|
added = apk['added']
|
||||||
|
if not lastupdated or apk['added'] > lastupdated:
|
||||||
|
lastupdated = apk['added']
|
||||||
|
|
||||||
|
if added:
|
||||||
|
app['added'] = added
|
||||||
|
else:
|
||||||
|
print "WARNING: Don't know when " + app['id'] + " was added"
|
||||||
|
if lastupdated:
|
||||||
|
app['lastupdated'] = lastupdated
|
||||||
|
else:
|
||||||
|
print "WARNING: Don't know when " + app['id'] + " was last updated"
|
||||||
|
|
||||||
|
if bestver == 0:
|
||||||
|
if app['Name'] is None:
|
||||||
|
app['Name'] = app['id']
|
||||||
|
app['icon'] = ''
|
||||||
|
if app['Disabled'] is None:
|
||||||
|
print "WARNING: Application " + app['id'] + " has no packages"
|
||||||
|
else:
|
||||||
|
if app['Name'] is None:
|
||||||
|
app['Name'] = bestapk['name']
|
||||||
|
app['icon'] = bestapk['icon']
|
||||||
|
|
||||||
|
# Sort the app list by name, then the web site doesn't have to by default.
|
||||||
|
# (we had to wait until we'd scanned the apks to do this, because mostly the
|
||||||
|
# name comes from there!)
|
||||||
|
apps = sorted(apps, key=lambda app: app['Name'].upper())
|
||||||
|
|
||||||
|
# Generate warnings for apk's with no metadata (or create skeleton
|
||||||
|
# metadata files, if requested on the command line)
|
||||||
|
for apk in apks:
|
||||||
|
found = False
|
||||||
|
for app in apps:
|
||||||
|
if app['id'] == apk['id']:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
if options.createmeta:
|
||||||
|
f = open(os.path.join('metadata', apk['id'] + '.txt'), 'w')
|
||||||
|
f.write("License:Unknown\n")
|
||||||
|
f.write("Web Site:\n")
|
||||||
|
f.write("Source Code:\n")
|
||||||
|
f.write("Issue Tracker:\n")
|
||||||
|
f.write("Summary:" + apk['name'] + "\n")
|
||||||
|
f.write("Description:\n")
|
||||||
|
f.write(apk['name'] + "\n")
|
||||||
|
f.write(".\n")
|
||||||
|
f.close()
|
||||||
|
print "Generated skeleton metadata for " + apk['id']
|
||||||
|
else:
|
||||||
|
print "WARNING: " + apk['apkname'] + " (" + apk['id'] + ") has no metadata"
|
||||||
|
print " " + apk['name'] + " - " + apk['version']
|
||||||
|
|
||||||
|
if len(repodirs) > 1:
|
||||||
|
archive_old_apks(apps, apks, repodirs[0], repodirs[1], archive_older)
|
||||||
|
|
||||||
|
make_index(apps, apks, repodirs[0], False, categories)
|
||||||
|
|
||||||
|
if len(repodirs) > 1:
|
||||||
|
apks, cc = scan_apks(apps, apkcache, repodirs[1], knownapks)
|
||||||
|
if cc:
|
||||||
|
cachechanged = True
|
||||||
|
make_index(apps, apks, repodirs[1], True, categories)
|
||||||
|
|
||||||
if update_stats:
|
if update_stats:
|
||||||
|
|
||||||
# Update known apks info...
|
# Update known apks info...
|
||||||
@ -748,19 +769,19 @@ def main():
|
|||||||
data += app['icon'] + "\t"
|
data += app['icon'] + "\t"
|
||||||
data += app['License'] + "\n"
|
data += app['License'] + "\n"
|
||||||
break
|
break
|
||||||
f = open('repo/latestapps.dat', 'w')
|
f = open(os.path.join(repodirs[0], 'latestapps.dat'), 'w')
|
||||||
f.write(data)
|
f.write(data)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
if cachechanged:
|
||||||
|
with open(apkcachefile, 'wb') as cf:
|
||||||
|
pickle.dump(apkcache, cf)
|
||||||
|
|
||||||
# Update the wiki...
|
# Update the wiki...
|
||||||
if options.wiki:
|
if options.wiki:
|
||||||
update_wiki(apps, apks, options.verbose)
|
update_wiki(apps, apks, options.verbose)
|
||||||
|
|
||||||
print "Finished."
|
print "Finished."
|
||||||
print str(apps_inrepo) + " apps in repo"
|
|
||||||
print str(apps_disabled) + " disabled"
|
|
||||||
print str(apps_nopkg) + " with no packages"
|
|
||||||
print str(warnings) + " warnings"
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user