Revision 5d3daee1
/dev/null | ||
---|---|---|
1 |
#!/bin/bash |
|
2 |
|
|
3 |
REMOTEUPSTREAM=develop |
|
4 |
REMOTEDEBIAN=debian-develop |
|
5 |
PKGAREA=~/packages |
|
6 |
BACKUPAREA=~/backup |
|
7 |
BUILDAREA=$(mktemp -d --tmpdir=/tmp build-area.XXX) |
|
8 |
|
|
9 |
PACKAGES=" |
|
10 |
snf-quotaholder-app |
|
11 |
snf-astakos-app |
|
12 |
snf-common |
|
13 |
snf-webproject |
|
14 |
snf-cyclades-app |
|
15 |
snf-cyclades-gtools |
|
16 |
snf-tools |
|
17 |
snf-pithos-app |
|
18 |
snf-pithos-backend |
|
19 |
snf-pithos-tools" |
|
20 |
|
/dev/null | ||
---|---|---|
1 |
# Copyright 2012 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
import git |
|
35 |
import os |
|
36 |
import sys |
|
37 |
from sh import mktemp, cd, rm, git_dch, python |
|
38 |
from optparse import OptionParser |
|
39 |
|
|
40 |
try: |
|
41 |
from colors import red, green |
|
42 |
except ImportError: |
|
43 |
red = lambda x: x |
|
44 |
green = lambda x: x |
|
45 |
|
|
46 |
print_red = lambda x: sys.stdout.write(red(x) + "\n") |
|
47 |
print_green = lambda x: sys.stdout.write(green(x) + "\n") |
|
48 |
|
|
49 |
AVAILABLE_MODES = ["release", "snapshot"] |
|
50 |
PACKAGES = ( |
|
51 |
"snf-astakos-app", |
|
52 |
"snf-common", |
|
53 |
"snf-webproject", |
|
54 |
"snf-cyclades-app", |
|
55 |
"snf-cyclades-gtools", |
|
56 |
"snf-tools", |
|
57 |
"snf-pithos-app", |
|
58 |
"snf-pithos-backend", |
|
59 |
"snf-pithos-tools", |
|
60 |
) |
|
61 |
|
|
62 |
|
|
63 |
def main(): |
|
64 |
parser = OptionParser(usage="usage: %prog [options] mode", |
|
65 |
version="%prog 1.0") |
|
66 |
parser.add_option("-k", "--keep-repo", |
|
67 |
action="store_true", |
|
68 |
dest="keep_repo", |
|
69 |
default=False, |
|
70 |
help="Do not delete the cloned repository") |
|
71 |
parser.add_option("-b", "--build-dir", |
|
72 |
dest="build_dir", |
|
73 |
default=None, |
|
74 |
help="Directory to store created pacakges") |
|
75 |
parser.add_option("-r", "--repo-dir", |
|
76 |
dest="repo_dir", |
|
77 |
default=None, |
|
78 |
help="Directory to clone repository") |
|
79 |
parser.add_option("-d", "--dirty", |
|
80 |
dest="force_dirty", |
|
81 |
default=False, |
|
82 |
action="store_true", |
|
83 |
help="Do not check if working directory is dirty") |
|
84 |
|
|
85 |
(options, args) = parser.parse_args() |
|
86 |
|
|
87 |
mode = args[0] |
|
88 |
if mode not in AVAILABLE_MODES: |
|
89 |
raise ValueError(red("Invalid argument! Mode must be one: %s" |
|
90 |
% ", ".join(AVAILABLE_MODES))) |
|
91 |
|
|
92 |
# Do not prompt for merge message. Required for some Git versions |
|
93 |
os.environ["GITFLOW_BUILD_MODE"] = mode |
|
94 |
|
|
95 |
try: |
|
96 |
original_repo = git.Repo(".") |
|
97 |
except git.git.InvalidGitRepositoryError: |
|
98 |
raise RuntimeError(red("Current directory is not git repository.")) |
|
99 |
|
|
100 |
if original_repo.is_dirty() and not options.force_dirty: |
|
101 |
toplevel = original_repo.working_dir |
|
102 |
raise RuntimeError(red("Repository %s is dirty." % toplevel)) |
|
103 |
|
|
104 |
repo_dir = options.repo_dir |
|
105 |
if not repo_dir: |
|
106 |
repo_dir = mktemp("-d", "/tmp/synnefo-build-repo-XXX").stdout.strip() |
|
107 |
print_green("Created temporary directory '%s' for the cloned repo." |
|
108 |
% repo_dir) |
|
109 |
|
|
110 |
repo = original_repo.clone(repo_dir) |
|
111 |
print_green("Cloned current repository to '%s'." % repo_dir) |
|
112 |
|
|
113 |
reflog_hexsha = repo.head.log()[-1].newhexsha |
|
114 |
print "Latest Reflog entry is %s" % reflog_hexsha |
|
115 |
|
|
116 |
branch = repo.head.reference.name |
|
117 |
if branch == "master": |
|
118 |
debian_branch = "debian" |
|
119 |
else: |
|
120 |
debian_branch = "debian-" + branch |
|
121 |
|
|
122 |
try: |
|
123 |
repo.references[debian_branch] |
|
124 |
except IndexError: |
|
125 |
# Branch does not exist |
|
126 |
# FIXME: remove hard-coded strings.. |
|
127 |
if branch == "debian": |
|
128 |
repo.git.branch("--track", debian_branch, "origin/debian") |
|
129 |
else: |
|
130 |
repo.git.branch("--track", debian_branch, "origin/debian-develop") |
|
131 |
|
|
132 |
repo.git.checkout(debian_branch) |
|
133 |
print_green("Changed to branch '%s'" % debian_branch) |
|
134 |
|
|
135 |
repo.git.merge(branch) |
|
136 |
print_green("Merged branch '%s' into '%s'" % (branch, debian_branch)) |
|
137 |
|
|
138 |
cd(repo_dir) |
|
139 |
version = python(repo_dir + "/devtools/version.py", "debian").strip() |
|
140 |
print_green("The new debian version will be: '%s'" % version) |
|
141 |
|
|
142 |
dch = git_dch("--debian-branch=%s" % debian_branch, |
|
143 |
"--git-author", |
|
144 |
"--ignore-regex=\".*\"", |
|
145 |
"--multimaint-merge", |
|
146 |
"--since=HEAD", |
|
147 |
"--new-version=%s" % version) |
|
148 |
print_green("Successfully ran '%s'" % " ".join(dch.cmd)) |
|
149 |
|
|
150 |
os.system("vim debian/changelog") |
|
151 |
repo.git.add("debian/changelog") |
|
152 |
|
|
153 |
if mode == "release": |
|
154 |
repo.git.commit("-s", "-a", "-m", "Bump new upstream version") |
|
155 |
if branch == "master": |
|
156 |
repo.git.tag("debian/" + version) |
|
157 |
|
|
158 |
for package in PACKAGES: |
|
159 |
# python setup.py should run in its directory |
|
160 |
cd(package) |
|
161 |
package_dir = repo_dir + "/" + package |
|
162 |
res = python(package_dir + "/setup.py", "sdist", _out=sys.stdout) |
|
163 |
cd("../") |
|
164 |
print res.stdout |
|
165 |
|
|
166 |
# Add version.py files to repo |
|
167 |
os.system("grep \"__version_vcs\" -r . -l -I | xargs git add -f") |
|
168 |
|
|
169 |
build_dir = options.build_dir |
|
170 |
if not options.build_dir: |
|
171 |
build_dir = mktemp("-d", "/tmp/synnefo-build-XXX").stdout.strip() |
|
172 |
print_green("Created directory '%s' to store the .deb files." % |
|
173 |
build_dir) |
|
174 |
|
|
175 |
os.system("git-buildpackage --git-export-dir=%s --git-upstream-branch=%s" |
|
176 |
" --git-debian-branch=%s --git-export=INDEX --git-ignore-new -sa" |
|
177 |
% (build_dir, branch, debian_branch)) |
|
178 |
|
|
179 |
if not options.keep_repo: |
|
180 |
print_green("Removing cloned repo '%s'." % repo_dir) |
|
181 |
rm("-r", repo_dir) |
|
182 |
else: |
|
183 |
print_green("Repository dir '%s'" % repo_dir) |
|
184 |
|
|
185 |
print_green("Completed. Version '%s', build area: '%s'" |
|
186 |
% (version, build_dir)) |
|
187 |
|
|
188 |
|
|
189 |
if __name__ == "__main__": |
|
190 |
main() |
/dev/null | ||
---|---|---|
1 |
#!/bin/bash |
|
2 |
|
|
3 |
# Copyright 2012 GRNET S.A. All rights reserved. |
|
4 |
# |
|
5 |
# Redistribution and use in source and binary forms, with or |
|
6 |
# without modification, are permitted provided that the following |
|
7 |
# conditions are met: |
|
8 |
# |
|
9 |
# 1. Redistributions of source code must retain the above |
|
10 |
# copyright notice, this list of conditions and the following |
|
11 |
# disclaimer. |
|
12 |
# |
|
13 |
# 2. Redistributions in binary form must reproduce the above |
|
14 |
# copyright notice, this list of conditions and the following |
|
15 |
# disclaimer in the documentation and/or other materials |
|
16 |
# provided with the distribution. |
|
17 |
# |
|
18 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
19 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
21 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
22 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
25 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
26 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
27 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
28 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
29 |
# POSSIBILITY OF SUCH DAMAGE. |
|
30 |
# |
|
31 |
# The views and conclusions contained in the software and |
|
32 |
# documentation are those of the authors and should not be |
|
33 |
# interpreted as representing official policies, either expressed |
|
34 |
# or implied, of GRNET S.A. |
|
35 |
|
|
36 |
parse_git_branch() |
|
37 |
{ |
|
38 |
git branch 2> /dev/null | grep '^*' | sed 's/^*\ //g' |
|
39 |
} |
|
40 |
|
|
41 |
die() |
|
42 |
{ |
|
43 |
echo $* 1>&2 |
|
44 |
exit 1 |
|
45 |
} |
|
46 |
|
|
47 |
cleanup() |
|
48 |
{ |
|
49 |
trap - EXIT |
|
50 |
|
|
51 |
if [ ${#CLEANUP[*]} -gt 0 ]; then |
|
52 |
LAST_ELEMENT=$((${#CLEANUP[*]}-1)) |
|
53 |
REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0) |
|
54 |
for i in $REVERSE_INDEXES; do |
|
55 |
local cmd=${CLEANUP[$i]} |
|
56 |
$cmd |
|
57 |
done |
|
58 |
fi |
|
59 |
} |
|
60 |
|
|
61 |
add_cleanup() { |
|
62 |
local cmd="" |
|
63 |
for arg; do cmd+=$(printf "%q " "$arg"); done |
|
64 |
CLEANUP+=("$cmd") |
|
65 |
} |
|
66 |
|
|
67 |
|
|
68 |
add_checkpoint() |
|
69 |
{ |
|
70 |
commit=$(git reflog | head -n1 | cut -f 1 -d " ") |
|
71 |
add_cleanup git reset --hard $commit |
|
72 |
LASTCHECKPOINT=$commit |
|
73 |
} |
|
74 |
|
|
75 |
CLEANUP=( ) |
|
76 |
|
|
77 |
source devtools/autopkg.conf |
|
78 |
|
|
79 |
# The root of the git repository, no matter where we're called from |
|
80 |
TOPLEVEL="$(git rev-parse --show-toplevel)" |
|
81 |
CURRENT_BRANCH=$(parse_git_branch) |
|
82 |
|
|
83 |
LOCALBRANCH="$CURRENT_BRANCH" |
|
84 |
LOCALDEBIAN=$1 |
|
85 |
DEBIANBRANCH=${LOCALDEBIAN:- origin/$REMOTEDEBIAN} |
|
86 |
|
|
87 |
MODIFIED=$(git status --short | grep -v "??") |
|
88 |
if [[ -n $MODIFIED ]]; then |
|
89 |
echo "error: Repository is dirty. Commit your local changes." |
|
90 |
exit 1 |
|
91 |
fi |
|
92 |
|
|
93 |
|
|
94 |
set -e |
|
95 |
trap cleanup EXIT |
|
96 |
|
|
97 |
cd "$TOPLEVEL" |
|
98 |
|
|
99 |
# Prerequisites: Test all important directories exist |
|
100 |
test -d "$PKGAREA" || die "Package area directory $PKGAREA missing" |
|
101 |
test -d "$BACKUPAREA" || die "Backup area directory $BACKUPAREA missing" |
|
102 |
|
|
103 |
# Prerequisite: Test the dialog utility is available |
|
104 |
dialog --help &>/dev/null || die "Could not run the 'dialog' utility" |
|
105 |
|
|
106 |
|
|
107 |
echo "##########################################################" |
|
108 |
echo "Will build packages" |
|
109 |
echo "under '$BUILDAREA'," |
|
110 |
echo "from local branch '$LOCALBRANCH'" |
|
111 |
echo "and debian branch '$DEBIANBRANCH'" |
|
112 |
echo "##########################################################" |
|
113 |
echo "Press Enter to continue..." |
|
114 |
read |
|
115 |
|
|
116 |
add_checkpoint |
|
117 |
|
|
118 |
# Create a temporary debian branch to do everything |
|
119 |
TMPDEBIAN=$(mktemp -u debian.XXX) |
|
120 |
|
|
121 |
git branch --track $TMPDEBIAN $DEBIANBRANCH |
|
122 |
#add_cleanup git branch -D $TMPDEBIAN |
|
123 |
|
|
124 |
git checkout $TMPDEBIAN |
|
125 |
add_cleanup git checkout $LOCALBRANCH |
|
126 |
|
|
127 |
add_checkpoint |
|
128 |
|
|
129 |
# Whether we are in snapshot or release mode |
|
130 |
snap=false |
|
131 |
mrgextra=-m |
|
132 |
dchextra=-R |
|
133 |
mrgmsg="Merge branch '$REMOTEUPSTREAM' into $REMOTEDEBIAN" |
|
134 |
dialog --yesno "Create Snapshot?" 5 20 && snap=true && GITFLOW_BUILD_MODE=snapshot && dchextra=-S && mrgextra= && mrgmsg= |
|
135 |
|
|
136 |
# merge local branch into tmp branch with a nice commit message, |
|
137 |
# so it can be pushed as is to upstream debian |
|
138 |
GIT_MERGE_AUTOEDIT=no |
|
139 |
git merge $mrgextra ${mrgextra:+"$mrgmsg"} $LOCALBRANCH |
|
140 |
|
|
141 |
# auto edit Debian changelog depending on Snapshot or Release mode |
|
142 |
export EDITOR=/usr/bin/vim |
|
143 |
|
|
144 |
# use the devtools to determine Debian version |
|
145 |
export GITFLOW_BUILD_MODE |
|
146 |
version=$(devtools/version.py debian) |
|
147 |
git-dch --debian-branch=$TMPDEBIAN --git-author --ignore-regex=".*" --multimaint-merge --since=HEAD -N $version |
|
148 |
git add debian/changelog |
|
149 |
|
|
150 |
# get version from the changelog |
|
151 |
# we add a git tag here, so setup.py sdist works as expected |
|
152 |
# FIXME: This is a workaround for the way Synnefo packages determine |
|
153 |
# the versions for their Python packages |
|
154 |
version=$(IFS="()" ; read x v x < debian/changelog ; echo $v) |
|
155 |
if ! $snap; then |
|
156 |
git commit -s -a -m "Bump new upstream version" |
|
157 |
TAGFILE=$(mktemp -t tag.XXX) |
|
158 |
add_cleanup rm $TAGFILE |
|
159 |
dialog --inputbox "New Debian Tag: " 5 30 "debian/$version" 2>$TAGFILE |
|
160 |
git tag $(<$TAGFILE) |
|
161 |
add_cleanup git tag -d $(<$TAGFILE) |
|
162 |
fi |
|
163 |
|
|
164 |
|
|
165 |
for p in $PACKAGES; do |
|
166 |
|
|
167 |
cd $p |
|
168 |
python setup.py sdist |
|
169 |
grep "__version_vcs" -r . -l -I | xargs git add -f |
|
170 |
cd - |
|
171 |
|
|
172 |
done |
|
173 |
|
|
174 |
# Build all packages |
|
175 |
git-buildpackage --git-export-dir="$BUILDAREA" \ |
|
176 |
--git-upstream-branch=$LOCALBRANCH \ |
|
177 |
--git-debian-branch=$TMPDEBIAN \ |
|
178 |
--git-export=INDEX \ |
|
179 |
--git-ignore-new -sa |
|
180 |
|
|
181 |
# do some dirty backup |
|
182 |
# pkgarea might be needed by auto-deploy tool |
|
183 |
rm -f "$PKGAREA"/* || true |
|
184 |
cp -v "$BUILDAREA"/* "$PKGAREA"/ || true |
|
185 |
cp -v "$BUILDAREA"/* "$BACKUPAREA"/ || true |
|
186 |
|
|
187 |
echo "###############################################" |
|
188 |
echo "#### SUCCESS ####" |
|
189 |
echo "###############################################" |
|
190 |
|
|
191 |
git fetch origin |
|
192 |
#check if your local branch is up-to-date |
|
193 |
commits_behind=$(git rev-list $LOCALBRANCH..origin/$REMOTEUPSTREAM | wc -l) |
|
194 |
if [ $commits_behind -ne 0 ]; then |
|
195 |
die "Your local branch is outdated!! Please run: git pull --rebase origin/$REMOTEUPSTREAM" |
|
196 |
fi |
|
197 |
commits_behind=$(git rev-list $DEBIANBRANCH..origin/$REMOTEDEBIAN | wc -l) |
|
198 |
if [ $commits_behind -ne 0 ]; then |
|
199 |
die "Your debian branch is outdated!! Please run: git pull --rebase origin/$REMOTEDEBIAN" |
|
200 |
fi |
|
201 |
|
|
202 |
trap - EXIT |
|
203 |
|
|
204 |
# Remove the added versions.py files |
|
205 |
git reset --hard HEAD |
|
206 |
# here we can push the commits to the remote debian branch as they are |
|
207 |
|
|
208 |
if ! $snap; then |
|
209 |
TAGS="--tags" |
|
210 |
fi |
|
211 |
echo "git push $TAGS origin $TMPDEBIAN:$REMOTEDEBIAN" |
|
212 |
echo "git checkout $LOCALBRANCH" |
|
213 |
echo "git push $TAGS origin $LOCALBRANCH:$REMOTEUPSTREAM" |
|
214 |
echo |
|
215 |
|
|
216 |
exit 0 |
/dev/null | ||
---|---|---|
1 |
#!/usr/bin/env python |
|
2 |
# |
|
3 |
# Copyright 2012 GRNET S.A. All rights reserved. |
|
4 |
# |
|
5 |
# Redistribution and use in source and binary forms, with or |
|
6 |
# without modification, are permitted provided that the following |
|
7 |
# conditions are met: |
|
8 |
# |
|
9 |
# 1. Redistributions of source code must retain the above |
|
10 |
# copyright notice, this list of conditions and the following |
|
11 |
# disclaimer. |
|
12 |
# |
|
13 |
# 2. Redistributions in binary form must reproduce the above |
|
14 |
# copyright notice, this list of conditions and the following |
|
15 |
# disclaimer in the documentation and/or other materials |
|
16 |
# provided with the distribution. |
|
17 |
# |
|
18 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
19 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
21 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
22 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
25 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
26 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
27 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
28 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
29 |
# POSSIBILITY OF SUCH DAMAGE. |
|
30 |
# |
|
31 |
# The views and conclusions contained in the software and |
|
32 |
# documentation are those of the authors and should not be |
|
33 |
# interpreted as representing official policies, either expressed |
|
34 |
# or implied, of GRNET S.A. |
|
35 |
# |
|
36 |
# |
|
37 |
|
|
38 |
"""Unit Tests for devtools.version |
|
39 |
|
|
40 |
Provides unit tests for module devtools.version, |
|
41 |
for automatic generation of version strings. |
|
42 |
|
|
43 |
""" |
|
44 |
|
|
45 |
import os |
|
46 |
import unittest |
|
47 |
from pkg_resources import parse_version |
|
48 |
from version import debian_version_from_python_version |
|
49 |
|
|
50 |
|
|
51 |
class DebianVersionObject(object): |
|
52 |
"""Object representing a Debian Version.""" |
|
53 |
def __init__(self, pyver): |
|
54 |
self.version = debian_version_from_python_version(pyver) |
|
55 |
|
|
56 |
def __str__(self): |
|
57 |
return self.version |
|
58 |
|
|
59 |
|
|
60 |
def debian_compare_versions(a, op, b): |
|
61 |
i = os.system("dpkg --compare-versions %s %s %s" % (a, op, b)) |
|
62 |
return i == 0 |
|
63 |
|
|
64 |
# Set ordering between DebianVersionObject objects, by adding |
|
65 |
# debian_compare_versions |
|
66 |
for op in ["lt", "le", "eq", "ne", "gt", "ge"]: |
|
67 |
def gen(op): |
|
68 |
def operator_func(self, other): |
|
69 |
return debian_compare_versions(self.version, op, other.version) |
|
70 |
return operator_func |
|
71 |
setattr(DebianVersionObject, "__%s__" % op, gen(op)) |
|
72 |
|
|
73 |
|
|
74 |
def _random_commit(): |
|
75 |
import random |
|
76 |
import string |
|
77 |
return "".join(random.choice(string.hexdigits) for n in xrange(8)).lower() |
|
78 |
|
|
79 |
|
|
80 |
# Add a random commit number at the end of snapshot versions |
|
81 |
def version_with_commit(parse_func, v): |
|
82 |
if "_" in v: |
|
83 |
return parse_func(v + "_" + _random_commit()) |
|
84 |
else: |
|
85 |
return parse_func(v) |
|
86 |
|
|
87 |
V = lambda v: version_with_commit(parse_version, v) |
|
88 |
D = lambda v: version_with_commit(DebianVersionObject, v) |
|
89 |
|
|
90 |
|
|
91 |
class TestVersionFunctions(unittest.TestCase): |
|
92 |
def setUp(self): |
|
93 |
self.version_orderings = ( |
|
94 |
("0.14next", ">", "0.14"), |
|
95 |
("0.14next", ">", "0.14rc7"), |
|
96 |
("0.14next", "<", "0.14.1"), |
|
97 |
("0.14rc6", "<", "0.14"), |
|
98 |
("0.14.2rc6", ">", "0.14.1"), |
|
99 |
("0.14next_150", "<", "0.14next"), |
|
100 |
("0.14.1next_150", "<", "0.14.1next"), |
|
101 |
("0.14.1_149", "<", "0.14.1"), |
|
102 |
("0.14.1_149", "<", "0.14.1_150"), |
|
103 |
("0.13next_102", "<", "0.13next"), |
|
104 |
("0.13next", "<", "0.14rc5_120"), |
|
105 |
("0.14rc3_120", "<", "0.14rc3"), |
|
106 |
# The following test fails, but version.python_version |
|
107 |
# will never try to produce such a version: |
|
108 |
# ("0.14rc3", "<", "0.14_1"), |
|
109 |
("0.14_120", "<", "0.14"), |
|
110 |
("0.14", "<", "0.14next_20"), |
|
111 |
("0.14next_20", "<", "0.14next"), |
|
112 |
) |
|
113 |
|
|
114 |
def test_python_versions(self): |
|
115 |
for a, op, b in self.version_orderings: |
|
116 |
res = compare(V, a, op, b) |
|
117 |
self.assertTrue(res, "Python version: %s %s %s" |
|
118 |
" is not True" % (a, op, b)) |
|
119 |
|
|
120 |
def test_debian_versions(self): |
|
121 |
for a, op, b in self.version_orderings: |
|
122 |
res = compare(D, a, op, b) |
|
123 |
self.assertTrue(res, "Debian version %s %s %s" |
|
124 |
" is not True" % (a, op, b)) |
|
125 |
|
|
126 |
|
|
127 |
def compare(function, a, op, b): |
|
128 |
import operator |
|
129 |
str_to_op = {"<": operator.lt, |
|
130 |
"<=": operator.le, |
|
131 |
"==": operator.eq, |
|
132 |
">": operator.gt, |
|
133 |
">=": operator.ge} |
|
134 |
try: |
|
135 |
return str_to_op[op](function(a), function(b)) |
|
136 |
except KeyError: |
|
137 |
raise ValueError("Unknown operator '%s'" % op) |
|
138 |
|
|
139 |
if __name__ == '__main__': |
|
140 |
unittest.main() |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
# |
|
34 |
|
|
35 |
import os |
|
36 |
import sys |
|
37 |
|
|
38 |
from contextlib import contextmanager |
|
39 |
from fabric.api import * |
|
40 |
from fabric.colors import * |
|
41 |
|
|
42 |
env.project_root = "./" |
|
43 |
env.develop = False |
|
44 |
env.autoremove = True |
|
45 |
|
|
46 |
env.packages = ['snf-common', 'snf-cyclades-app', 'snf-cyclades-gtools', |
|
47 |
'snf-webproject', 'snf-pithos-backend', 'snf-pithos-app', |
|
48 |
'snf-pithos-tools', 'snf-astakos-app'] |
|
49 |
|
|
50 |
env.capture = False |
|
51 |
env.colors = True |
|
52 |
env.pypi_root = 'pypi' |
|
53 |
env.roledefs = { |
|
54 |
'docs': ['docs.dev.grnet.gr'], |
|
55 |
'pypi': ['docs.dev.grnet.gr'] |
|
56 |
} |
|
57 |
|
|
58 |
|
|
59 |
# colored logging |
|
60 |
notice = lambda x: sys.stdout.write(yellow(x) + "\n") |
|
61 |
info = lambda x: sys.stdout.write(green(x) + "\n") |
|
62 |
error = lambda x: sys.stdout.write(red(x) + "\n") |
|
63 |
|
|
64 |
|
|
65 |
def dev(): |
|
66 |
env.develop = True |
|
67 |
|
|
68 |
|
|
69 |
# wrap local to respect global capturing setting from env.capture |
|
70 |
oldlocal = local |
|
71 |
|
|
72 |
|
|
73 |
def local(cmd, capture="default"): |
|
74 |
if capture != "default": |
|
75 |
capture = capture |
|
76 |
else: |
|
77 |
capture = env.capture |
|
78 |
return oldlocal(cmd, capture=capture) |
|
79 |
|
|
80 |
|
|
81 |
def package_root(p): |
|
82 |
return os.path.join(env.project_root, p) |
|
83 |
|
|
84 |
|
|
85 |
def remove_pkg(p): |
|
86 |
notice("uninstalling package: %s" % p) |
|
87 |
with lcd(package_root(p)): |
|
88 |
with settings(warn_only=True): |
|
89 |
local("pip uninstall %s -y" % p, env.capture) |
|
90 |
|
|
91 |
|
|
92 |
def build_pkg(p): |
|
93 |
info("building package: %s" % p) |
|
94 |
with lcd(package_root(p)): |
|
95 |
local("if [ -d dist ]; then rm -r dist; fi;") |
|
96 |
local("if [ -d build ]; then rm -r build; fi;") |
|
97 |
local("python setup.py egg_info -d sdist") |
|
98 |
|
|
99 |
|
|
100 |
def install_pkg(p): |
|
101 |
info("installing package: %s" % p) |
|
102 |
with lcd(package_root(p)): |
|
103 |
if env.develop: |
|
104 |
local("python setup.py develop") |
|
105 |
else: |
|
106 |
local("python setup.py install") |
|
107 |
|
|
108 |
|
|
109 |
def install(*packages): |
|
110 |
for p in packages: |
|
111 |
install_pkg("snf-%s" % p) |
|
112 |
|
|
113 |
|
|
114 |
def buildall(): |
|
115 |
for p in env.packages: |
|
116 |
build_pkg(p) |
|
117 |
collectdists() |
|
118 |
|
|
119 |
|
|
120 |
def installall(): |
|
121 |
for p in env.packages: |
|
122 |
install_pkg(p) |
|
123 |
|
|
124 |
|
|
125 |
def collectdists(): |
|
126 |
if os.path.exists("./packages"): |
|
127 |
notice("removing 'packages' directory") |
|
128 |
local("rm -r packages") |
|
129 |
|
|
130 |
local("mkdir packages") |
|
131 |
for p in env.packages: |
|
132 |
local("cp %s/dist/*.tar.gz ./packages/" % package_root(p)) |
|
133 |
|
|
134 |
|
|
135 |
def removeall(): |
|
136 |
for p in env.packages: |
|
137 |
remove_pkg(p) |
|
138 |
|
|
139 |
|
|
140 |
def remove(*packages): |
|
141 |
for p in packages: |
|
142 |
remove_pkg("snf-%s" % p) |
|
143 |
|
|
144 |
|
|
145 |
# |
|
146 |
# GIT helpers |
|
147 |
# |
|
148 |
|
|
149 |
|
|
150 |
def git(params, locl=True): |
|
151 |
cmd = local if locl else run |
|
152 |
return cmd("git %s" % params, capture=True) |
|
153 |
|
|
154 |
|
|
155 |
def branch(): |
|
156 |
return git("symbolic-ref HEAD").split("/")[-1] |
|
157 |
|
|
158 |
|
|
159 |
@contextmanager |
|
160 |
def co(c): |
|
161 |
current_branch = branch() |
|
162 |
git("checkout %s" % c) |
|
163 |
# Use a try block to make sure we checkout the original branch. |
|
164 |
try: |
|
165 |
yield |
|
166 |
finally: |
|
167 |
try: |
|
168 |
git("checkout %s" % current_branch) |
|
169 |
except Exception: |
|
170 |
error("Could not checkout %s, you're still left at %s" % c) |
|
171 |
|
|
172 |
# |
|
173 |
# Debian packaging helpers |
|
174 |
# |
|
175 |
|
|
176 |
env.debian_branch = 'debian' |
|
177 |
env.deb_packages = ['snf-common', 'snf-cyclades-app', 'snf-cyclades-gtools', |
|
178 |
'snf-webproject', 'snf-pithos-backend', 'snf-pithos-tools', |
|
179 |
'snf-pithos-app', 'snf-astakos-app'] |
|
180 |
env.signdebs = True |
|
181 |
env.debrelease = False # Increase release number in Debian changelogs |
|
182 |
|
|
183 |
def _last_commit(f): |
|
184 |
return local("git rev-list --all --date-order --max-count=1 %s" % f, |
|
185 |
capture=True).strip() |
|
186 |
|
|
187 |
|
|
188 |
def _diff_from_master(c,f): |
|
189 |
return local("git log --oneline %s..master %s" \ |
|
190 |
" | wc -l" % (c, f), capture=True) |
|
191 |
|
|
192 |
|
|
193 |
def dch(p): |
|
194 |
with co(env.debian_branch): |
|
195 |
local("git merge master") |
|
196 |
local("git merge %s" % env.upstream) |
|
197 |
with lcd(package_root(p)): |
|
198 |
local("if [ ! -d .git ]; then mkdir .git; fi") |
|
199 |
|
|
200 |
# FIXME: |
|
201 |
# Checking for new changes in packages |
|
202 |
# has been removed temporarily. |
|
203 |
# Always create a new Debian changelog entry. |
|
204 |
## Check for new changes in package dir |
|
205 |
#diff = _diff_from_master(_last_commit("debian/changelog"), ".") |
|
206 |
#vercmd = "git describe --tags --abbrev=0"\ |
|
207 |
# " | sed -rn '\''s/^v(.*)/\\1/p'\''" |
|
208 |
#version = local(vercmd, capture=True) |
|
209 |
#if int(diff) > 0: |
|
210 |
if True: |
|
211 |
# Run git-dch in snapshot mode. |
|
212 |
# TODO: Support a --release mode in fabfile |
|
213 |
if not env.debrelease: |
|
214 |
notice(("Producing snapshot changelog entry, " |
|
215 |
"use 'debrelease' to produce release entries.")) |
|
216 |
local(("git-dch --debian-branch=%s --auto %s" % |
|
217 |
(env.debian_branch, |
|
218 |
"--release" if env.debrelease else "--snapshot"))) |
|
219 |
local(("git commit debian/changelog" |
|
220 |
" -m 'Updated %s changelog'" % p)) |
|
221 |
notice(("Make sure to tag Debian release in %s" % |
|
222 |
env.debian_branch)) |
|
223 |
|
|
224 |
local("rmdir .git") |
|
225 |
|
|
226 |
|
|
227 |
def dchall(): |
|
228 |
for p in env.deb_packages: |
|
229 |
info("updating debian changelog for package: %s" % p) |
|
230 |
dch(p) |
|
231 |
|
|
232 |
|
|
233 |
def debrelease(): |
|
234 |
env.debrelease = True |
|
235 |
|
|
236 |
|
|
237 |
def signdebs(): |
|
238 |
env.signdebs = True |
|
239 |
|
|
240 |
|
|
241 |
# Commands which automatically add and reset the version files which are not tracked by |
|
242 |
# git. Those version files are created from each setup.py using the synnefo-common |
|
243 |
# update_version, so we execute `python setup.py clean` to ensure that file gets |
|
244 |
# created and git add will not fail. The reset of those files after each build |
|
245 |
# certifies that succeded git checkouts will not fail due to existing local |
|
246 |
# changes. |
|
247 |
add_versions_cmd = "find . -regextype posix-egrep -regex \".*version.py$|.*\/synnefo\/versions\/.*py$\" -exec git add -f {} \;" |
|
248 |
reset_versions_cmd = "find . -regextype posix-egrep -regex \".*version.py$|.*\/synnefo\/versions\/.*py$\" -exec git reset {} \;" |
|
249 |
|
|
250 |
|
|
251 |
def builddeb(p, master="master", branch="debian-0.8"): |
|
252 |
with co(branch): |
|
253 |
info("Building debian package for %s" % p) |
|
254 |
with lcd(package_root(p)): |
|
255 |
local("git merge master") |
|
256 |
local("if [ ! -d .git ]; then mkdir .git; fi") |
|
257 |
local("python setup.py clean") |
|
258 |
local(add_versions_cmd) |
|
259 |
local(("git-buildpackage --git-upstream-branch=%s --git-debian-branch=%s" |
|
260 |
" --git-export=INDEX --git-ignore-new %s") % |
|
261 |
(master, branch, "" if env.signdebs else "-us -uc")) |
|
262 |
local("rm -rf .git") |
|
263 |
local(reset_versions_cmd) |
|
264 |
info("Done building debian package for %s" % p) |
|
265 |
|
|
266 |
|
|
267 |
def builddeball(b="debian-0.8"): |
|
268 |
for p in env.deb_packages: |
|
269 |
builddeb(p=p, branch=b) |
|
270 |
|
|
271 |
|
|
272 |
@roles('pypi') |
|
273 |
def uploadtars(): |
|
274 |
put("packages/*.tar.gz", 'www/pypi/') |
|
275 |
|
|
276 |
|
|
277 |
def cleandocs(): |
|
278 |
""" |
|
279 |
Remove _build directories for each doc project |
|
280 |
""" |
|
281 |
|
|
282 |
# docs contains conf.py in root directory |
|
283 |
if os.path.exists("docs/docs/_build"): |
|
284 |
local("rm -r docs/docs/_build") |
|
285 |
|
|
286 |
for p in env.packages: |
|
287 |
buildpth = os.path.join(package_root(p), 'docs', '_build') |
|
288 |
if os.path.exists(buildpth): |
|
289 |
local('rm -r %s' % buildpth) |
|
290 |
|
|
291 |
|
|
292 |
def builddocs(): |
|
293 |
""" |
|
294 |
Run sphinx builder for each project separately |
|
295 |
""" |
|
296 |
builddocs_cmd = "sphinx-build -b html -d _build/doctrees . _build/html" |
|
297 |
|
|
298 |
# docs contains conf.py in root directory |
|
299 |
with lcd("docs"): |
|
300 |
local(builddocs_cmd) |
|
301 |
|
|
302 |
for p in env.packages: |
|
303 |
info("Building %s docs" % p) |
|
304 |
docspth = os.path.join(package_root(p), 'docs') |
|
305 |
if os.path.exists(docspth): |
|
306 |
with lcd(docspth): |
|
307 |
local(builddocs_cmd) |
/dev/null | ||
---|---|---|
1 |
#!/bin/bash |
|
2 |
# |
|
3 |
# |
|
4 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
5 |
# |
|
6 |
# Redistribution and use in source and binary forms, with or |
|
7 |
# without modification, are permitted provided that the following |
|
8 |
# conditions are met: |
|
9 |
# |
|
10 |
# 1. Redistributions of source code must retain the above |
|
11 |
# copyright notice, this list of conditions and the following |
|
12 |
# disclaimer. |
|
13 |
# |
|
14 |
# 2. Redistributions in binary form must reproduce the above |
|
15 |
# copyright notice, this list of conditions and the following |
|
16 |
# disclaimer in the documentation and/or other materials |
|
17 |
# provided with the distribution. |
|
18 |
# |
|
19 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
20 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
21 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
22 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
23 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
26 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
27 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
28 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
29 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
30 |
# POSSIBILITY OF SUCH DAMAGE. |
|
31 |
# |
|
32 |
# The views and conclusions contained in the software and |
|
33 |
# documentation are those of the authors and should not be |
|
34 |
# interpreted as representing official policies, either expressed |
|
35 |
# or implied, of GRNET S.A. |
|
36 |
# |
|
37 |
|
|
38 |
set -e |
|
39 |
|
|
40 |
rm -rf env |
|
41 |
virtualenv --no-site-packages -ppython2.6 env |
|
42 |
source env/bin/activate |
|
43 |
export PIP_DOWNLOAD_CACHE=/tmp/.pip_cache |
|
44 |
pip install -r requirements.pip |
|
45 |
|
|
46 |
cd snf-common |
|
47 |
rm -rf build dist |
|
48 |
python setup.py install |
|
49 |
cd ../snf-cyclades-app |
|
50 |
rm -rf build dist |
|
51 |
python setup.py install |
|
52 |
cd ../snf-cyclades-gtools |
|
53 |
rm -rf build dist |
|
54 |
python setup.py install |
|
55 |
|
|
56 |
|
|
57 |
cd ../env |
|
58 |
# avoid vncauthproxy errors |
|
59 |
rm bin/vncauthproxy.py |
|
60 |
echo "running django tests..." >&2 |
|
61 |
export SYNNEFO_SETTINGS_DIR=/etc/lala |
|
62 |
snf-manage test admin api db logic userdata --settings=synnefo.settings.test |
|
63 |
cd .. |
|
64 |
deactivate |
|
65 |
|
|
66 |
#rm -rf env |
|
67 |
#virtualenv --no-site-packages -ppython2.7 env |
|
68 |
#source env/bin/activate |
|
69 |
#export PIP_DOWNLOAD_CACHE=/tmp/.pip_cache |
|
70 |
#pip install -r requirements.pip |
|
71 |
|
|
72 |
#cd snf-common |
|
73 |
#rm -rf build dist |
|
74 |
#python setup.py install |
|
75 |
#cd ../snf-cyclades-app |
|
76 |
#rm -rf build dist |
|
77 |
#python setup.py install |
|
78 |
#cd ../snf-cyclades-gtools |
|
79 |
#rm -rf build dist |
|
80 |
#python setup.py install |
|
81 |
|
|
82 |
#cd env |
|
83 |
## avoid vncauthproxy errors |
|
84 |
#rm bin/vncauthproxy.py |
|
85 |
#echo "running django tests..." >&2 |
|
86 |
#snf-manage test aai admin api db helpdesk invitations logic userdata --settings=synnefo.settings.test |
|
87 |
#cd .. |
|
88 |
#deactivate |
|
89 |
#rm -rf env |
/dev/null | ||
---|---|---|
1 |
#!/bin/bash |
|
2 |
# |
|
3 |
# |
|
4 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
5 |
# |
|
6 |
# Redistribution and use in source and binary forms, with or |
|
7 |
# without modification, are permitted provided that the following |
|
8 |
# conditions are met: |
|
9 |
# |
|
10 |
# 1. Redistributions of source code must retain the above |
|
11 |
# copyright notice, this list of conditions and the following |
|
12 |
# disclaimer. |
|
13 |
# |
|
14 |
# 2. Redistributions in binary form must reproduce the above |
|
15 |
# copyright notice, this list of conditions and the following |
|
16 |
# disclaimer in the documentation and/or other materials |
|
17 |
# provided with the distribution. |
|
18 |
# |
|
19 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
20 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
21 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
22 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
23 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
26 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
27 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
28 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
29 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
30 |
# POSSIBILITY OF SUCH DAMAGE. |
|
31 |
# |
|
32 |
# The views and conclusions contained in the software and |
|
33 |
# documentation are those of the authors and should not be |
|
34 |
# interpreted as representing official policies, either expressed |
|
35 |
# or implied, of GRNET S.A. |
|
36 |
# |
|
37 |
|
|
38 |
set -e |
|
39 |
|
|
40 |
echo "Running snf-cyclades-app tests..." >&2 |
|
41 |
snf-manage test admin api db logic userdata --settings=synnefo.settings.test |
|
42 |
|
|
43 |
echo "Running snf-cyclades-gtools tests..." >&2 |
|
44 |
./snf-cyclades-gtools/test/synnefo.ganeti_unittest.py |
|
45 |
|
/dev/null | ||
---|---|---|
1 |
#!/usr/bin/env python |
|
2 |
# |
|
3 |
# Copyright (C) 2010, 2011, 2012 GRNET S.A. All rights reserved. |
|
4 |
# |
|
5 |
# Redistribution and use in source and binary forms, with or |
|
6 |
# without modification, are permitted provided that the following |
|
7 |
# conditions are met: |
|
8 |
# |
|
9 |
# 1. Redistributions of source code must retain the above |
|
10 |
# copyright notice, this list of conditions and the following |
|
11 |
# disclaimer. |
|
12 |
# |
|
13 |
# 2. Redistributions in binary form must reproduce the above |
|
14 |
# copyright notice, this list of conditions and the following |
|
15 |
# disclaimer in the documentation and/or other materials |
|
16 |
# provided with the distribution. |
|
17 |
# |
|
18 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
19 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
21 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
22 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
25 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
26 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
27 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
28 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
29 |
# POSSIBILITY OF SUCH DAMAGE. |
|
30 |
# |
|
31 |
# The views and conclusions contained in the software and |
|
32 |
# documentation are those of the authors and should not be |
|
33 |
# interpreted as representing official policies, either expressed |
|
34 |
# or implied, of GRNET S.A. |
|
35 |
|
|
36 |
|
|
37 |
import os |
|
38 |
import re |
|
39 |
import sys |
|
40 |
import pprint |
|
41 |
import subprocess |
|
42 |
import git |
|
43 |
|
|
44 |
from distutils import log |
|
45 |
from collections import namedtuple |
|
46 |
|
|
47 |
|
|
48 |
# Branch types: |
|
49 |
# builds_snapshot: Whether the branch can produce snapshot builds |
|
50 |
# builds_release: Whether the branch can produce release builds |
|
51 |
# versioned: Whether the name of the branch defines a specific version |
|
52 |
# allowed_version_re: A regular expression describing allowed values for |
|
53 |
# base_version in this branch |
|
54 |
branch_type = namedtuple("branch_type", ["builds_snapshot", "builds_release", |
|
55 |
"versioned", "allowed_version_re"]) |
|
56 |
VERSION_RE = "[0-9]+\.[0-9]+" |
|
57 |
BRANCH_TYPES = { |
|
58 |
"feature": branch_type(True, False, False, "^%snext$" % VERSION_RE), |
|
59 |
"develop": branch_type(True, False, False, "^%snext$" % VERSION_RE), |
|
60 |
"release": branch_type(True, True, True, |
|
61 |
"^(?P<bverstr>%s)rc[1-9][0-9]*$" % VERSION_RE), |
|
62 |
"master": branch_type(False, True, False, |
|
63 |
"^%s$" % VERSION_RE), |
|
64 |
"hotfix": branch_type(True, True, True, |
|
65 |
"^(?P<bverstr>^%s\.[1-9][0-9]*)$" % VERSION_RE)} |
|
66 |
BASE_VERSION_FILE = "version" |
|
67 |
|
|
68 |
|
|
69 |
def get_commit_id(commit, current_branch): |
|
70 |
"""Return the commit ID |
|
71 |
|
|
72 |
If the commit is a 'merge' commit, and one of the parents is a |
|
73 |
debian branch we return a compination of the parents commits. |
|
74 |
|
|
75 |
""" |
|
76 |
def short_id(commit): |
|
77 |
return commit.hexsha[0:7] |
|
78 |
|
|
79 |
parents = commit.parents |
|
80 |
cur_br_name = current_branch.name |
|
81 |
if len(parents) == 1: |
|
82 |
return short_id(commit) |
|
83 |
elif len(parents) == 2: |
|
84 |
if cur_br_name.startswith("debian-") or cur_br_name == "debian": |
|
85 |
pr1, pr2 = parents |
|
86 |
return short_id(pr1) + "~" + short_id(pr2) |
|
87 |
else: |
|
88 |
return short_id(commit) |
|
89 |
else: |
|
90 |
raise RuntimeError("Commit %s has more than 2 parents!" % commit) |
|
91 |
|
|
92 |
|
|
93 |
def vcs_info(): |
|
94 |
""" |
|
95 |
Return current git HEAD commit information. |
|
96 |
|
|
97 |
Returns a tuple containing |
|
98 |
- branch name |
|
99 |
- commit id |
|
100 |
- commit count |
|
101 |
- git describe output |
|
102 |
- path of git toplevel directory |
|
103 |
|
|
104 |
""" |
|
105 |
try: |
|
106 |
repo = git.Repo(".") |
|
107 |
branch = repo.head.reference |
|
108 |
revid = get_commit_id(branch.commit, branch) |
|
109 |
revno = len(list(repo.iter_commits())) |
|
110 |
desc = repo.git.describe("--tags") |
|
111 |
toplevel = repo.working_dir |
|
112 |
except git.InvalidGitRepositoryError: |
|
113 |
log.error("Could not retrieve git information. " + |
|
114 |
"Current directory not a git repository?") |
|
115 |
return None |
|
116 |
|
|
117 |
info = namedtuple("vcs_info", ["branch", "revid", "revno", |
|
118 |
"desc", "toplevel"]) |
|
119 |
|
|
120 |
return info(branch=branch.name, revid=revid, revno=revno, desc=desc, |
|
121 |
toplevel=toplevel) |
|
122 |
|
|
123 |
|
|
124 |
def base_version(vcs_info): |
|
125 |
"""Determine the base version from a file in the repository""" |
|
126 |
|
|
127 |
f = open(os.path.join(vcs_info.toplevel, BASE_VERSION_FILE)) |
|
128 |
lines = [l.strip() for l in f.readlines()] |
|
129 |
l = [l for l in lines if not l.startswith("#")] |
|
130 |
if len(l) != 1: |
|
131 |
raise ValueError("File '%s' should contain a single non-comment line.") |
|
132 |
return l[0] |
|
133 |
|
|
134 |
|
|
135 |
def build_mode(): |
|
136 |
"""Determine the build mode from the value of $GITFLOW_BUILD_MODE""" |
|
137 |
try: |
|
138 |
mode = os.environ["GITFLOW_BUILD_MODE"] |
|
139 |
assert mode == "release" or mode == "snapshot" |
|
140 |
except (KeyError, AssertionError): |
|
141 |
raise ValueError("GITFLOW_BUILD_MODE environment variable must be " |
|
142 |
"'release' or 'snapshot'") |
|
143 |
return mode |
|
144 |
|
|
145 |
|
|
146 |
def python_version(base_version, vcs_info, mode): |
|
147 |
"""Generate a Python distribution version following devtools conventions. |
|
148 |
|
|
149 |
This helper generates a Python distribution version from a repository |
|
150 |
commit, following devtools conventions. The input data are: |
|
151 |
* base_version: a base version number, presumably stored in text file |
|
152 |
inside the repository, e.g., /version |
|
153 |
* vcs_info: vcs information: current branch name and revision no |
|
154 |
* mode: "snapshot", or "release" |
|
155 |
|
|
156 |
This helper assumes a git branching model following: |
|
157 |
http://nvie.com/posts/a-successful-git-branching-model/ |
|
158 |
|
|
159 |
with 'master', 'develop', 'release-X', 'hotfix-X' and 'feature-X' branches. |
|
160 |
|
|
161 |
General rules: |
|
162 |
a) any repository commit can get as a Python version |
|
163 |
b) a version is generated either in 'release' or in 'snapshot' mode |
|
164 |
c) the choice of mode depends on the branch, see following table. |
|
165 |
|
|
166 |
A python version is of the form A_NNN, |
|
167 |
where A: X.Y.Z{,next,rcW} and NNN: a revision number for the commit, |
|
168 |
as returned by vcs_info(). |
|
169 |
|
|
170 |
For every combination of branch and mode, releases are numbered as follows: |
|
171 |
|
|
172 |
BRANCH: / MODE: snapshot release |
|
173 |
-------- ------------------------------ |
|
174 |
feature 0.14next_150 N/A |
|
175 |
develop 0.14next_151 N/A |
|
176 |
release 0.14rc2_249 0.14rc2 |
|
177 |
master N/A 0.14 |
|
178 |
hotfix 0.14.1rc6_121 0.14.1rc6 |
|
179 |
N/A 0.14.1 |
|
180 |
|
|
181 |
The suffix 'next' in a version name is used to denote the upcoming version, |
|
182 |
the one being under development in the develop and release branches. |
|
183 |
Version '0.14next' is the version following 0.14, and only lives on the |
|
184 |
develop and feature branches. |
|
185 |
|
|
186 |
The suffix 'rc' is used to denote release candidates. 'rc' versions live |
|
187 |
only in release and hotfix branches. |
|
188 |
|
|
189 |
Suffixes 'next' and 'rc' have been chosen to ensure proper ordering |
|
190 |
according to setuptools rules: |
|
191 |
|
|
192 |
http://www.python.org/dev/peps/pep-0386/#setuptools |
|
193 |
|
|
194 |
Every branch uses a value for A so that all releases are ordered based |
|
195 |
on the branch they came from, so: |
|
196 |
|
|
197 |
So |
|
198 |
0.13next < 0.14rcW < 0.14 < 0.14next < 0.14.1 |
|
199 |
|
|
200 |
and |
|
201 |
|
|
202 |
>>> V("0.14next") > V("0.14") |
|
203 |
True |
|
204 |
>>> V("0.14next") > V("0.14rc7") |
|
205 |
True |
|
206 |
>>> V("0.14next") > V("0.14.1") |
|
207 |
False |
|
208 |
>>> V("0.14rc6") > V("0.14") |
|
209 |
False |
|
210 |
>>> V("0.14.2rc6") > V("0.14.1") |
|
211 |
True |
|
212 |
|
|
213 |
The value for _NNN is chosen based of the revision number of the specific |
|
214 |
commit. It is used to ensure ascending ordering of consecutive releases |
|
215 |
from the same branch. Every version of the form A_NNN comes *before* |
|
216 |
than A: All snapshots are ordered so they come before the corresponding |
|
217 |
release. |
|
218 |
|
|
219 |
So |
|
220 |
0.14next_* < 0.14 |
|
221 |
0.14.1_* < 0.14.1 |
|
222 |
etc |
|
223 |
|
|
224 |
and |
|
225 |
|
|
226 |
>>> V("0.14next_150") < V("0.14next") |
|
227 |
True |
|
228 |
>>> V("0.14.1next_150") < V("0.14.1next") |
|
229 |
True |
|
230 |
>>> V("0.14.1_149") < V("0.14.1") |
|
231 |
True |
|
232 |
>>> V("0.14.1_149") < V("0.14.1_150") |
|
233 |
True |
|
234 |
|
|
235 |
Combining both of the above, we get |
|
236 |
0.13next_* < 0.13next < 0.14rcW_* < 0.14rcW < 0.14_* < 0.14 |
|
237 |
< 0.14next_* < 0.14next < 0.14.1_* < 0.14.1 |
|
238 |
|
|
239 |
and |
|
240 |
|
|
241 |
>>> V("0.13next_102") < V("0.13next") |
|
242 |
True |
|
243 |
>>> V("0.13next") < V("0.14rc5_120") |
|
244 |
True |
|
245 |
>>> V("0.14rc3_120") < V("0.14rc3") |
|
246 |
True |
|
247 |
>>> V("0.14rc3") < V("0.14_1") |
|
248 |
True |
|
249 |
>>> V("0.14_120") < V("0.14") |
|
250 |
True |
|
251 |
>>> V("0.14") < V("0.14next_20") |
|
252 |
True |
|
253 |
>>> V("0.14next_20") < V("0.14next") |
|
254 |
True |
|
255 |
|
|
256 |
Note: one of the tests above fails because of constraints in the way |
|
257 |
setuptools parses version numbers. It does not affect us because the |
|
258 |
specific version format that triggers the problem is not contained in the |
|
259 |
table showing allowed branch / mode combinations, above. |
|
260 |
|
|
261 |
|
|
262 |
""" |
|
263 |
|
|
264 |
branch = vcs_info.branch |
|
265 |
|
|
266 |
# If it's a debian branch, ignore starting "debian-" |
|
267 |
brnorm = branch |
|
268 |
if brnorm == "debian": |
|
269 |
brnorm = "debian-master" |
|
270 |
if brnorm.startswith("debian-"): |
|
271 |
brnorm = brnorm.replace("debian-", "", 1) |
|
272 |
|
|
273 |
# Sanity checks |
|
274 |
if "-" in brnorm: |
|
275 |
btypestr = brnorm.split("-")[0] |
|
276 |
else: |
|
277 |
btypestr = brnorm |
|
278 |
|
|
279 |
try: |
|
280 |
btype = BRANCH_TYPES[btypestr] |
|
281 |
except KeyError: |
|
282 |
allowed_branches = ", ".join(x for x in BRANCH_TYPES.keys()) |
|
283 |
raise ValueError("Malformed branch name '%s', cannot classify as one " |
|
284 |
"of %s" % (btypestr, allowed_branches)) |
|
285 |
|
|
286 |
if btype.versioned: |
|
287 |
try: |
|
288 |
bverstr = brnorm.split("-")[1] |
|
289 |
except IndexError: |
|
290 |
# No version |
|
291 |
raise ValueError("Branch name '%s' should contain version" % |
|
292 |
branch) |
|
293 |
|
|
294 |
# Check that version is well-formed |
|
295 |
if not re.match(VERSION_RE, bverstr): |
|
296 |
raise ValueError("Malformed version '%s' in branch name '%s'" % |
|
297 |
(bverstr, branch)) |
|
298 |
|
|
299 |
m = re.match(btype.allowed_version_re, base_version) |
|
300 |
if not m or (btype.versioned and m.groupdict()["bverstr"] != bverstr): |
|
301 |
raise ValueError("Base version '%s' unsuitable for branch name '%s'" % |
|
302 |
(base_version, branch)) |
|
303 |
|
|
304 |
if mode not in ["snapshot", "release"]: |
|
305 |
raise ValueError("Specified mode '%s' should be one of 'snapshot' or " |
|
306 |
"'release'" % mode) |
|
307 |
snap = (mode == "snapshot") |
|
308 |
|
|
309 |
if ((snap and not btype.builds_snapshot) or |
|
310 |
(not snap and not btype.builds_release)): |
|
311 |
raise ValueError("Invalid mode '%s' in branch type '%s'" % |
|
312 |
(mode, btypestr)) |
|
313 |
|
|
314 |
if snap: |
|
315 |
v = "%s_%d_%s" % (base_version, vcs_info.revno, vcs_info.revid) |
|
316 |
else: |
|
317 |
v = base_version |
|
318 |
return v |
|
319 |
|
|
320 |
|
|
321 |
def debian_version_from_python_version(pyver): |
|
322 |
"""Generate a debian package version from a Python version. |
|
323 |
|
|
324 |
This helper generates a Debian package version from a Python version, |
|
325 |
following devtools conventions. |
|
326 |
|
|
327 |
Debian sorts version strings differently compared to setuptools: |
|
328 |
http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version |
|
329 |
|
|
330 |
Initial tests: |
|
331 |
|
|
332 |
>>> debian_version("3") < debian_version("6") |
|
333 |
True |
|
334 |
>>> debian_version("3") < debian_version("2") |
|
335 |
False |
|
336 |
>>> debian_version("1") == debian_version("1") |
|
337 |
True |
|
338 |
>>> debian_version("1") != debian_version("1") |
|
339 |
False |
|
340 |
>>> debian_version("1") >= debian_version("1") |
|
341 |
True |
|
342 |
>>> debian_version("1") <= debian_version("1") |
|
343 |
True |
|
344 |
|
|
345 |
This helper defines a 1-1 mapping between Python and Debian versions, |
|
346 |
with the same ordering. |
|
347 |
|
|
348 |
Debian versions are ordered in the same way as Python versions: |
|
349 |
|
|
350 |
>>> D("0.14next") > D("0.14") |
|
351 |
True |
|
352 |
>>> D("0.14next") > D("0.14rc7") |
|
353 |
True |
|
354 |
>>> D("0.14next") > D("0.14.1") |
|
355 |
False |
|
356 |
>>> D("0.14rc6") > D("0.14") |
|
357 |
False |
|
358 |
>>> D("0.14.2rc6") > D("0.14.1") |
|
359 |
True |
|
360 |
|
|
361 |
and |
|
362 |
|
|
363 |
>>> D("0.14next_150") < D("0.14next") |
|
364 |
True |
|
365 |
>>> D("0.14.1next_150") < D("0.14.1next") |
|
366 |
True |
|
367 |
>>> D("0.14.1_149") < D("0.14.1") |
|
368 |
True |
|
369 |
>>> D("0.14.1_149") < D("0.14.1_150") |
|
370 |
True |
|
371 |
|
|
372 |
and |
|
373 |
|
|
374 |
>>> D("0.13next_102") < D("0.13next") |
|
375 |
True |
|
376 |
>>> D("0.13next") < D("0.14rc5_120") |
|
377 |
True |
|
378 |
>>> D("0.14rc3_120") < D("0.14rc3") |
|
379 |
True |
|
380 |
>>> D("0.14rc3") < D("0.14_1") |
|
381 |
True |
|
382 |
>>> D("0.14_120") < D("0.14") |
|
383 |
True |
|
384 |
>>> D("0.14") < D("0.14next_20") |
|
385 |
True |
|
386 |
>>> D("0.14next_20") < D("0.14next") |
|
387 |
True |
|
388 |
|
|
389 |
""" |
|
390 |
return pyver.replace("_", "~").replace("rc", "~rc") + "-1" |
|
391 |
|
|
392 |
|
|
393 |
def debian_version(base_version, vcs_info, mode): |
|
394 |
p = python_version(base_version, vcs_info, mode) |
|
395 |
return debian_version_from_python_version(p) |
|
396 |
|
|
397 |
|
|
398 |
def user_info(): |
|
399 |
import getpass |
|
400 |
import socket |
|
401 |
return "%s@%s" % (getpass.getuser(), socket.getfqdn()) |
|
402 |
|
|
403 |
|
|
404 |
def update_version(module, name="version", root="."): |
|
405 |
""" |
|
406 |
Generate or replace version.py as a submodule of `module`. |
|
407 |
|
|
408 |
This is a helper to generate/replace a version.py file containing version |
|
409 |
information as a submodule of passed `module`. |
|
410 |
|
|
411 |
""" |
|
412 |
|
|
413 |
v = vcs_info() |
|
414 |
if not v: |
|
415 |
# Return early if not in development environment |
|
416 |
return |
|
417 |
b = base_version(v) |
|
418 |
mode = build_mode() |
|
419 |
paths = [root] + module.split(".") + ["%s.py" % name] |
|
420 |
module_filename = os.path.join(*paths) |
|
421 |
version = python_version(b, v, mode) |
|
422 |
content = """ |
|
423 |
__version__ = "%(version)s" |
|
424 |
__version_info__ = %(version_info)s |
|
425 |
__version_vcs_info__ = %(vcs_info)s |
|
426 |
__version_user_info__ = "%(user_info)s" |
|
427 |
""" % dict(version=version, version_info=version.split("."), |
|
428 |
vcs_info=pprint.PrettyPrinter().pformat(dict(v._asdict())), |
|
429 |
user_info=user_info()) |
|
430 |
|
|
431 |
module_file = file(module_filename, "w+") |
|
432 |
module_file.write(content) |
|
433 |
module_file.close() |
|
434 |
|
|
435 |
|
|
436 |
if __name__ == "__main__": |
Also available in: Unified diff