Rewrite set_version in python

For the following reasons:

- integration of https://github.com/openSUSE/\
   obs-service-set_version_pysemver simpler
- testsuite is in python
- try "import this" to get more reasons :)
This commit is contained in:
Thomas Bechtold 2015-07-03 20:56:55 +02:00
parent 5013040029
commit b5cadd8c1e
2 changed files with 152 additions and 212 deletions

View file

@ -10,6 +10,6 @@ python:
install:
- pip install flake8 ddt unittest2 --use-mirrors
script:
- flake8 tests/
- flake8 set_version tests/
- python -m unittest discover tests/

View file

@ -1,244 +1,184 @@
#!/usr/bin/perl
#!/usr/bin/python
# -*- coding: utf-8 -*-
# A simple script to update version number in spec, dsc or arch linux files
#
# (C) 2010 by Adrian Schröter <adrian@suse.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# See http://www.gnu.org/licenses/gpl-2.0.html for full license text.
# (C) 2015 by Thomas Bechtold <tbechtold@suse.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# See http://www.gnu.org/licenses/gpl-2.0.html for full license text.
import argparse
import glob
import os
import re
import shutil
import sys
import tarfile
import zipfile
my $version;
my $basename="";
my $outdir;
my @files;
use strict;
use warnings;
use Data::Dumper;
use File::Basename;
suffixes = ('tar', 'tar.gz', 'tgz', 'tar.bz2', 'tbz2', 'tar.xz', 'zip')
suffixes_re = "|".join(map(lambda x: re.escape(x), suffixes))
sub usage()
{
print<<END
Open Build Service source service "set_version"
def _get_local_files():
""" sorted local file list by modification time (newest first)"""
files = glob.glob('*')
files.sort(key=lambda x: os.stat(os.path.join(os.getcwd(), x)).st_mtime,
reverse=True)
return files
Used to update build description files with a detected or given version number.
Required:
def _get_version_via_filename(files, basename):
""" detect version based on file names"""
for f in files:
regex = r"^%s.*[-_]([\d].*)\.(?:%s)$" % (re.escape(basename),
suffixes_re)
m = re.match(regex, f)
if m:
return m.group(1)
# Nothing found
return None
--outdir: out put directory for modified sources
Options:
def _get_version(str_list, basename):
regex = "%s.*[-_]([\d][^\/]*).*" % basename
for s in str_list:
m = re.match(regex, s)
if m:
return m.group(1)
# Nothing found
return None
--version: use given version string, do not detect it from source files
--basename: detect version based on the file name with a given prefix
def _get_version_via_archive_dirname(files, basename):
""" detect version based tar'd directory name"""
for f in filter(lambda x: x.endswith(suffixes), files):
# handle tarfiles
if tarfile.is_tarfile(f):
with tarfile.open(f) as tf:
v = _get_version(tf.getnames(), basename)
if v:
return v
# handle zipfiles
if zipfile.is_zipfile(f):
with zipfile.ZipFile(f, 'r') as zf:
v = _get_version(zf.namelist(), basename)
if v:
return v
# Nothing found
return None
--file: modify only this build description. maybe used multiple times.
END
;
exit;
}
while (@ARGV) {
usage() if $ARGV[0] eq '--help';
if ($ARGV[0] eq '--outdir') {
shift @ARGV;
$outdir = shift @ARGV;
next;
} elsif ($ARGV[0] eq '--version') {
shift @ARGV;
$version = shift @ARGV;
next;
} elsif ($ARGV[0] eq '--basename') {
# this is actually more a prefix
shift @ARGV;
$basename = shift @ARGV;
next;
} elsif ($ARGV[0] eq '--file') {
shift @ARGV;
push @files, shift @ARGV;
next;
} else {
die("Unknown argument $ARGV[0]!");
}
last;
}
def _get_version_via_debian_changelog(filename):
# from http://anonscm.debian.org/cgit/pkg-python-debian/\
# python-debian.git/tree/lib/debian/changelog.py
topline = re.compile(r'^(\w%(name_chars)s*) \(([^\(\) \t]+)\)'
'((\s+%(name_chars)s+)+)\;'
% {'name_chars': '[-+0-9a-z.]'},
re.IGNORECASE)
if os.path.exists(filename):
with open(filename, "r") as f:
firstline = f.readline()
topmatch = topline.match(firstline)
if topmatch:
return topmatch.group(2)
# Nothing found
return None
usage() unless $outdir;
# get local file list
local *D;
opendir(D, ".") || return ();
my @srcfiles = grep {$_ ne '.' && $_ ne '..'} readdir(D);
closedir D;
def _replace_tag(filename, tag, string):
# first, modify a copy of filename and then move it
filename_new = ".%s" % filename
shutil.copy(filename, filename_new)
with open(filename_new, 'r+') as f:
contents = f.read()
f.seek(0)
if filename.endswith("PKGBUILD"):
contents_new, subs = re.subn(
r"^{tag}=.*".format(tag=tag),
r"{tag}={string}".format(tag=tag, string=string), contents)
else:
# keep inline macros for rpm
contents_new, subs = re.subn(r'^{tag}:(\s*)[^%]*'.format(tag=tag),
r'{tag}:\g<1>{string}'.format(
tag=tag, string=string), contents)
# sorted local file list by modification time (newest first)
@srcfiles = sort { -M "$a" <=> -M "$b" } (@srcfiles);
if subs > 0:
f.truncate()
f.write(contents_new)
os.rename(filename_new, filename)
# Detect version based on file names
unless ($version) {
my @binsufs = qw{tar tar.gz tgz tar.bz2 tbz2 tar.xz zip};
my $binsufsre = join('|', map {"\Q$_\E"} @binsufs);
for my $name (@srcfiles) {
if ($name =~ /^$basename.*[-_]([\d].*)\.(?:$binsufsre)$/) {
$version=$1;
last;
}
}
}
# Detect version based tar'd directory name
unless ($version) {
my @binsufs = qw{tar tar.gz tgz tar.bz2 tbz2 tar.xz zip};
my $binsufsre = join('|', map {"\Q$_\E"} @binsufs);
for my $name (@srcfiles) {
if ($name =~ /$binsufsre$/) {
open( FH, "tar tf $name |" );
my $line;
while (defined($line = <FH>)) {
if ($line =~ /$basename.*[-_]([\d][^\/]*)\/.*/) {
$version=$1;
last;
}
}
close( FH );
def _replace_debian_changelog_version(filename, version_new):
# first, modify a copy of filename and then move it
filename_new = ".%s" % filename
shutil.copy(filename, filename_new)
# get current version
version_current = _get_version_via_debian_changelog(filename_new)
with open(filename_new, 'r+') as f:
content_lines = f.readlines()
f.seek(0)
content_lines[0] = content_lines[0].replace(
version_current, version_new, 1)
f.truncate()
f.writelines(content_lines)
os.rename(filename_new, filename)
last if $version;
}
}
}
# Detect version based on Debian changelog
unless ($version) {
if (defined(my $dch = glob("*debian.changelog"))) {
open ( FH, "$dch" );
my $line = <FH>;
if ($line =~ /.*\((.*)\).*/) {
$version=$1;
}
close( FH );
}
}
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Open Build Service source service "set_version".'
'Used to update build description files with a '
'detected or given version number.')
parser.add_argument('--outdir', required=True,
help='output directory for modified sources')
parser.add_argument('--version',
help='use given version string, do not detect it '
'from source files')
parser.add_argument('--basename', default="",
help='detect version based on the file name with '
'a given prefix')
parser.add_argument('--file', action='append',
help='modify only this build description. '
'maybe used multiple times.')
args = vars(parser.parse_args())
# to replace tags in .spec and .dsc files
sub replace_tag {
my ($filename, $tag, $string) = @_;
local *R;
local *W;
# read, try first an modified version in outdir
if (!open(R, '<', "$outdir/$filename")) {
if (!open(R, '<', $filename)) {
die("unable to read $filename: $!\n");
return undef;
}
}
if (!open(W, '>', "$outdir/.$filename")) {
die("unable to write .$filename: $!\n");
return undef;
}
my $line;
while (defined($line = <R>)) {
$line =~ s/\n$//;
if ( $filename =~ /PKGBUILD$/ ) {
$line =~ s/^$tag=.*/$tag=$string/;
} else {
# keep inline macros for rpm
$line =~ s/^$tag:(\s*)[^%]*/$tag:$1$string/;
}
print W "$line\n";
}
close R;
close W;
version = args['version']
rename("$outdir/.$filename", "$outdir/$filename") || die("rename failed");
}
files_local = _get_local_files()
# to replace version in debian.changelog file
sub replace_debian_changelog_version {
my ($filename, $string) = @_;
local *R;
local *W;
# read, try first an modified version in outdir
if (!open(R, '<', "$outdir/$filename")) {
if (!open(R, '<', $filename)) {
die("unable to read $filename: $!\n");
return undef;
}
}
if (!open(W, '>', "$outdir/.$filename")) {
die("unable to write .$filename: $!\n");
return undef;
}
my $line = <R>;
# only replace version in the most recent entry (the first line)
if ($line =~ /.*\((.*)\).*/) {
$line =~ s/$1/$string/;
}
print W "$line";
# copy the rest
while (<R>) {
print W;
}
close R;
close W;
if not version:
version = _get_version_via_filename(
files_local, args["basename"])
if not version:
version = _get_version_via_archive_dirname(
files_local, args["basename"])
if not version:
version = _get_version_via_debian_changelog("debian.changelog")
rename("$outdir/.$filename", "$outdir/$filename") || die("rename failed");
}
if not version:
sys.exit(-1)
die("No version found or defined") unless $version;
# if no files explicitly specified process whole directory
files = args['file'] or files_local
# if no files explicitly specified process whole directory
push @files, @srcfiles if @files <= 0;
# handle rpm spec and debian dsc files
for f in filter(lambda x: x.endswith((".spec", ".dsc")), files):
_replace_tag(f, 'Version', version)
_replace_tag(f, 'Release', "0")
# handle rpm spec and debian dsc files
for my $file (grep {$_ =~ /.(spec|dsc)$/} @files) {
replace_tag($file, 'Version', $version);
replace_tag($file, 'Release', "0");
}
# update debian.changelog file
for f in filter(lambda x: x.endswith(("debian.changelog")), files):
_replace_debian_changelog_version(f, version)
# update debian.changelog file
for my $file (grep {$_ =~ /.*debian.changelog$/} @files) {
replace_debian_changelog_version($file, $version);
}
use Digest::MD5;
# handle arch linux PKGBUILD files
for my $pkgbuild (grep {$_ =~ /PKGBUILD$/} @files) {
# find md5sum of tar ball
my $md5sum;
my $tarfile;
for my $file (@srcfiles) {
if ( $file =~ /^_service:.*[-_]\Q$version\E.*/ ) {
$tarfile = $file;
last;
}
}
unless($tarfile) {
for my $file (@srcfiles) {
if ( $file =~ /.*[-_]\Q$version\E.*/ ) {
$tarfile = $file;
last;
}
}
}
open(FILE, $tarfile) || die "Unable to find source file to calculate md5sum\n";
my $ctx = Digest::MD5->new;
$ctx->addfile(*FILE);
$md5sum = $ctx->hexdigest;
close(FILE);
die ("Failed to calculate md5sum") unless $md5sum;
replace_tag($pkgbuild, "pkgver", $version);
replace_tag($pkgbuild, "pkgrel", "0");
replace_tag($pkgbuild, "md5sums", "('".$md5sum."')");
}
# handle arch linux PKGBUILD files
# TODO: Handle the md5sums generation!
for f in filter(lambda x: x.endswith(("PKGBUILD")), files):
_replace_tag(f, "md5sums", "(SKIP)")