From ec5836583771f6cb048aaf4f84ecef947523554d Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 6 Nov 2009 23:17:36 -0500 Subject: [PATCH] - add support for importing tarballs - show list of files in a tarball in the web UI - show more information about archive files, if it's available --- docs/schema.sql | 1 + hub/kojihub.py | 59 +++++++++++++++++++++++++++++++------- www/kojiweb/fileinfo.chtml | 21 ++++++++++++++ www/lib/kojiweb/util.py | 30 +++++++++++++++++++ 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/docs/schema.sql b/docs/schema.sql index 6bbabdc7..9941f376 100644 --- a/docs/schema.sql +++ b/docs/schema.sql @@ -652,6 +652,7 @@ CREATE TABLE archivetypes ( insert into archivetypes (name, description, extensions) values ('jar', 'Jar files', 'jar war rar ear'); insert into archivetypes (name, description, extensions) values ('zip', 'Zip archives', 'zip'); insert into archivetypes (name, description, extensions) values ('pom', 'Maven Project Object Management files', 'pom'); +insert into archivetypes (name, description, extensions) values ('tar', 'Tar files', 'tar tar.gz tar.bz2'); -- Do we want to enforce a constraint that a build can only generate one -- archive with a given name? diff --git a/hub/kojihub.py b/hub/kojihub.py index c3c9b574..04243cd3 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -44,6 +44,7 @@ import sha import stat import subprocess import sys +import tarfile import tempfile import time import types @@ -3285,10 +3286,39 @@ def _get_zipfile_list(archive_id, zippath): archive = zipfile.ZipFile(zippath, 'r') for entry in archive.infolist(): filename = koji.fixEncoding(entry.filename) - size = entry.file_size result.append({'archive_id': archive_id, 'name': filename, - 'size': size}) + 'size': entry.file_size, + 'mtime': int(time.mktime(entry.date_time + (0, 0, -1)))}) + archive.close() + return result + +def _get_tarball_list(archive_id, tarpath): + """ + Get a list of the entries in the tarball located at tarpath. + Return a list of dicts, one per entry in the tarball. Each dict contains: + - archive_id + - name + - size + - mtime + - mode + - user + - group + If the file does not exist, return an empty list. + """ + result = [] + if not os.path.exists(tarpath): + return result + archive = tarfile.open(tarpath, 'r') + for entry in archive: + filename = koji.fixEncoding(entry.name) + result.append({'archive_id': archive_id, + 'name': filename, + 'size': entry.size, + 'mtime': entry.mtime, + 'mode': entry.mode, + 'user': entry.uname, + 'group': entry.gname}) archive.close() return result @@ -3304,14 +3334,7 @@ def list_archive_files(archive_id, queryOpts=None): archive_info = get_archive(archive_id, strict=True) archive_type = get_archive_type(type_id=archive_info['type_id'], strict=True) - if not archive_type['name'] in ('zip', 'jar'): - # XXX support other archive types - return _applyQueryOpts([], queryOpts) - - build_info = get_build(archive_info['build_id']) - if not build_info: - return _applyQueryOpts([], queryOpts) - + build_info = get_build(archive_info['build_id'], strict=True) maven_info = get_maven_build(build_info['id']) if not maven_info: # XXX support other archive types, when they exist @@ -3319,7 +3342,14 @@ def list_archive_files(archive_id, queryOpts=None): file_path = os.path.join(koji.pathinfo.mavenbuild(build_info, maven_info), archive_info['filename']) - return _applyQueryOpts(_get_zipfile_list(archive_id, file_path), queryOpts) + + if archive_type['name'] in ('zip', 'jar'): + return _applyQueryOpts(_get_zipfile_list(archive_id, file_path), queryOpts) + elif archive_type['name'] == 'tar': + return _applyQueryOpts(_get_tarball_list(archive_id, file_path), queryOpts) + else: + # XXX support other archive types + return _applyQueryOpts([], queryOpts) def get_archive_file(archive_id, filename): """ @@ -3956,6 +3986,12 @@ def _import_wrapper(task_id, build_info, rpm_results): import_build_log(os.path.join(rpm_task_dir, log), build_info, subdir='noarch') +def get_archive_types(): + """Return a list of all supported archivetypes""" + select = """SELECT id, name, description, extensions FROM archivetypes + ORDER BY id""" + return _multiRow(select, {}, ('id', 'name', 'description', 'extensions')) + def _get_archive_type_by_name(name, strict=True): select = """SELECT id, name, description, extensions FROM archivetypes WHERE name = %(name)s""" @@ -6209,6 +6245,7 @@ class RootExports(object): getBuild = staticmethod(get_build) getMavenBuild = staticmethod(get_maven_build) + getArchiveTypes = staticmethod(get_archive_types) getArchiveType = staticmethod(get_archive_type) listArchives = staticmethod(list_archives) getArchive = staticmethod(get_archive) diff --git a/www/kojiweb/fileinfo.chtml b/www/kojiweb/fileinfo.chtml index 7ee33e23..d8002d8d 100644 --- a/www/kojiweb/fileinfo.chtml +++ b/www/kojiweb/fileinfo.chtml @@ -1,5 +1,6 @@ #from kojiweb import util #import urllib +#import datetime #include "includes/header.chtml" #if $rpm @@ -20,6 +21,26 @@ Size$file.size + #if $file.has_key('mtime') and $file.mtime + + Modification time$util.formatTimeLong($datetime.datetime.fromtimestamp($file.mtime)) + + #end if + #if $file.has_key('user') and $file.user + + User$file.user + + #end if + #if $file.has_key('group') and $file.group + + Group$file.group + + #end if + #if $file.has_key('mode') and $file.mode + + Mode$util.formatMode($file.mode) + + #end if #if $rpm Flags diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 3e24e3b4..d3e90ee8 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -3,6 +3,7 @@ import datetime import koji from koji.util import md5_constructor import os +import stat import time #a bunch of exception classes that explainError needs from socket import error as socket_error @@ -349,6 +350,35 @@ def formatDep(name, version, flags): s = "%s %s" %(s, version) return s +def formatMode(mode): + """Format a numeric mode into a ls-like string describing the access mode.""" + if stat.S_ISREG(mode): + result = '-' + elif stat.S_ISDIR(mode): + result = 'd' + elif stat.S_ISCHR(mode): + result = 'c' + elif stat.S_ISBLK(mode): + result = 'b' + elif stat.S_ISFIFO(mode): + result = 'p' + elif stat.S_ISLNK(mode): + result = 'l' + elif stat.S_ISSOCK(mode): + result = 's' + else: + # What is it? Show it like a regular file. + result = '-' + + for x in ('USR', 'GRP', 'OTH'): + for y in ('R', 'W', 'X'): + if mode & getattr(stat, 'S_I' + y + x): + result += y.lower() + else: + result += '-' + + return result + def rowToggle(template): """If the value of template._rowNum is even, return 'row-even'; if it is odd, return 'row-odd'. Increment the value before checking it.