1 #!/usr/bin/env python
2 #-*- coding: utf-8 -*-
3 import os
4 import re
5 import sys
6 import shutil
7 import tarfile
8 import subprocess
9 import logging
10 from optparse import OptionParser
11 from os.path import join
12 from tempfile import mkdtemp
14 # Version setter
15 VERSION = "1.0"
16 methods = {}
19 # Check if pysvn is installed
20 try:
21 import pysvn
23 def load_svn(base_url, release, tmp_dir):
24 source = base_url + "/" + release
25 svn = pysvn.Client()
27 for part in ["gosa-core", "gosa-plugins"]:
28 print("Retrieving %s from SVN..." % part)
29 rev = svn.export(source + "/" + part, join(tmp_dir, part))
31 return "svn" + str(rev.number)
33 methods["svn"] = load_svn
34 except:
35 pass
38 def tar_extract(name, target):
39 tar = tarfile.open(name, "r:gz")
40 tar.extractall(target)
41 tar.close()
43 def tar_create(name, source):
44 tar = tarfile.open(name, "w:gz")
45 tar.add(source)
46 tar.close()
48 def build(release, base_url, target="/tmp", method="svn", key_id=None, cleanup=True):
50 # Absolutize target
51 target = os.path.abspath(target)
53 # Create target and temporary directory
54 try:
55 tmp_dir = mkdtemp()
56 target_dir = join(target, "gosa")
57 os.makedirs(target_dir)
59 # Get data
60 if method in methods:
61 rev = methods[method](base_url, release, tmp_dir)
62 else:
63 print("Error: method '%s' is notavailable" % method)
65 # Prepare gosa-core for archiving
66 core_dir = join(tmp_dir, "gosa", "gosa-core")
67 os.makedirs(join(tmp_dir, "gosa"))
68 shutil.move(join(tmp_dir, "gosa-core"), join(tmp_dir, "gosa"))
69 shutil.move(join(core_dir, "debian"), target_dir)
71 # Remove all but .php files provided by GOsa
72 smarty_dir = join(core_dir, "include", "smarty")
73 for rfile in [f for f in os.listdir(join(smarty_dir, "plugins"))
74 if not f in ["block.render.php", "block.t.php", "function.msgPool.php",
75 "function.factory.php"]]:
77 os.remove(join(smarty_dir, "plugins", rfile))
79 for rfile in [join(smarty_dir, f) for f in os.listdir(smarty_dir)]:
80 if os.path.basename(rfile) != "plugins":
81 if os.path.isdir(rfile):
82 shutil.rmtree(rfile)
83 else:
84 os.remove(rfile)
86 # Get target version number
87 version = None
88 with open(join(core_dir, "Changelog")) as f:
89 for line in f.readlines():
90 if line.startswith("* gosa"):
91 version = line.split(" ")[2].strip()
92 break
94 # If we're in trunk or branches, add revision to version
95 if re.match(r"^(trunk|branches/)", release):
96 version = "%s+%s" % (version, rev)
97 os.chdir(target_dir)
98 subprocess.call(["dch", "--newversion=" + version + "-1",
99 "--no-force-save-on-release", "Development snapshot"])
101 # Build gosa-core tar file
102 os.chdir(tmp_dir)
103 tar_create(join(target, "gosa_%s.orig.tar.gz" % version), "gosa")
105 # Clean up and build plugin archives
106 tars = []
107 os.chdir(join(tmp_dir, "gosa-plugins"))
108 for f in os.listdir(join(tmp_dir, "gosa-plugins")):
109 pth = join(target, "gosa_%s.orig-%s.tar.gz" % (version, f))
110 if os.path.exists(join(tmp_dir, "gosa-plugins", f, "plugin.dsc")):
111 os.remove(join(tmp_dir, "gosa-plugins", f, "plugin.dsc"))
112 tar_create(pth, f)
113 tars.append(pth)
115 # Stage build directory
116 tar_extract(join(target, "gosa_%s.orig.tar.gz" % version), target)
117 for tar_file in tars:
118 tar_extract(tar_file, target_dir)
120 # Build packages
121 print("Building packages to %s" % target)
122 os.chdir(target_dir)
123 if key_id:
124 status = subprocess.call(["dpkg-buildpackage", "-k", key_id, "-rfakeroot"])
125 else:
126 status = subprocess.call(["dpkg-buildpackage", "-uc", "-us", "-rfakeroot"])
128 # Bail out?
129 if status:
130 raise Exception("Build failed: exit code %s" % status)
132 except Exception as detail:
133 print("Error:", str(detail))
135 # Cleanup
136 finally:
137 if cleanup:
138 if os.path.exists(tmp_dir):
139 shutil.rmtree(tmp_dir)
140 if os.path.exists(target_dir):
141 shutil.rmtree(target_dir)
143 def main():
144 # Sanity check
145 for cli, pkg in [("/usr/bin/dpkg-buildpackage", "dpkg-dev"),
146 ("/usr/bin/fakeroot", "fakeroot"),
147 ("/usr/bin/dch", "devscripts"),
148 ("/usr/bin/dh", "debhelper")]:
150 if not os.path.exists(cli):
151 print("Error: %s is missing, please install %s" % (cli, pkg))
152 exit(1)
154 # Methods available?
155 if not methods:
156 print("Error: no retrieval methods available, please install python-svn")
157 exit(1)
159 # Parse commandline options
160 op = OptionParser(usage="%prog - GOsa packaging aid",
161 version="%prog " + VERSION)
162 op.add_option("-s", "--source", dest="source",
163 default="https://oss.gonicus.de/repositories/gosa",
164 help="retrieval base path [%default]", metavar="URL")
165 op.add_option("-t", "--target", dest="target",
166 default=".",
167 help="target directory [%default]", metavar="PATH")
168 op.add_option("-m", "--method", dest="method",
169 default="svn",
170 help="retrieval method [%default]", metavar="METHOD")
171 op.add_option("-n", "--no-cleanup", action="store_false",
172 dest="cleanup", default=True,
173 help="don't clean up temporary files")
174 op.add_option("-k", "--key-id", dest="key_id",
175 default=None,
176 help="GPG key ID used for signing if applicable", metavar="KEY_ID")
178 (options, args) = op.parse_args()
179 if len(args) != 1:
180 op.print_help()
181 exit(1)
183 # Allow version shortcut, but prepend tags/
184 release = args[0]
185 if re.match(r"^[0-9]", release):
186 release = "tags/" + release
188 # Validate release
189 if not re.match(r"^(tags/.|branches/.|trunk$)", release):
190 print("Error: release must be in the format tags/x (or just x), branches/x or trunk")
191 exit(1)
193 # Start build
194 build(release, options.source, options.target,
195 options.method, options.key_id, options.cleanup)
198 """ Main GOsa packaging aid """
199 if __name__ == '__main__':
200 if not sys.stdout.encoding:
201 sys.stdout = codecs.getwriter('utf8')(sys.stdout)
202 if not sys.stderr.encoding:
203 sys.stderr = codecs.getwriter('utf8')(sys.stderr)
205 main()