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, smarty=False):
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 if not smarty:
73 smarty_dir = join(core_dir, "include", "smarty")
74 for rfile in [f for f in os.listdir(join(smarty_dir, "plugins"))
75 if not f in ["block.render.php", "block.t.php", "function.msgPool.php",
76 "function.factory.php", "function.image.php"]]:
78 os.remove(join(smarty_dir, "plugins", rfile))
80 for rfile in [join(smarty_dir, f) for f in os.listdir(smarty_dir)]:
81 if os.path.basename(rfile) != "plugins":
82 if os.path.isdir(rfile):
83 shutil.rmtree(rfile)
84 else:
85 os.remove(rfile)
87 # Remove smarty3 package section
88 out = ""
89 control = join(tmp_dir, "debian", "control")
90 data = open(control, 'r')
91 remove = False
93 for line in data:
94 line = line.strip()
95 if line == "Package: smarty3":
96 remove = True
98 if not remove:
99 out += line + "\n"
101 if remove and line == "":
102 remove = False
104 of = open(control, 'w')
105 of.write(out)
106 of.close()
108 # Get target version number
109 version = None
110 with open(join(core_dir, "Changelog")) as f:
111 for line in f.readlines():
112 if line.startswith("* gosa"):
113 version = line.split(" ")[2].strip()
114 break
116 # If we're in trunk or branches, add revision to version
117 if re.match(r"^(trunk|branches/)", release):
118 version = "%s+%s" % (version, rev)
119 os.chdir(target_dir)
120 subprocess.call(["dch", "--newversion=" + version + "-1",
121 "--no-force-save-on-release", "Development snapshot"])
123 # Build gosa-core tar file
124 os.chdir(tmp_dir)
125 tar_create(join(target, "gosa_%s.orig.tar.gz" % version), "gosa")
127 # Clean up and build plugin archives
128 tars = []
129 os.chdir(join(tmp_dir, "gosa-plugins"))
130 for f in os.listdir(join(tmp_dir, "gosa-plugins")):
131 pth = join(target, "gosa_%s.orig-%s.tar.gz" % (version, f))
132 if os.path.exists(join(tmp_dir, "gosa-plugins", f, "plugin.dsc")):
133 os.remove(join(tmp_dir, "gosa-plugins", f, "plugin.dsc"))
134 tar_create(pth, f)
135 tars.append(pth)
137 # Stage build directory
138 tar_extract(join(target, "gosa_%s.orig.tar.gz" % version), target)
139 for tar_file in tars:
140 tar_extract(tar_file, target_dir)
142 # Build packages
143 print("Building packages to %s" % target)
144 os.chdir(target_dir)
145 if key_id:
146 status = subprocess.call(["dpkg-buildpackage", "-k", key_id, "-rfakeroot"])
147 else:
148 status = subprocess.call(["dpkg-buildpackage", "-uc", "-us", "-rfakeroot"])
150 # Bail out?
151 if status:
152 raise Exception("Build failed: exit code %s" % status)
154 except Exception as detail:
155 print("Error:", str(detail))
157 # Cleanup
158 finally:
159 if cleanup:
160 if os.path.exists(tmp_dir):
161 shutil.rmtree(tmp_dir)
162 if os.path.exists(target_dir):
163 shutil.rmtree(target_dir)
165 def main():
166 # Sanity check
167 for cli, pkg in [("/usr/bin/dpkg-buildpackage", "dpkg-dev"),
168 ("/usr/bin/fakeroot", "fakeroot"),
169 ("/usr/bin/dch", "devscripts"),
170 ("/usr/bin/dh", "debhelper")]:
172 if not os.path.exists(cli):
173 print("Error: %s is missing, please install %s" % (cli, pkg))
174 exit(1)
176 # Methods available?
177 if not methods:
178 print("Error: no retrieval methods available, please install python-svn")
179 exit(1)
181 # Parse commandline options
182 op = OptionParser(usage="%prog - GOsa packaging aid",
183 version="%prog " + VERSION)
184 op.add_option("-s", "--source", dest="source",
185 default="https://oss.gonicus.de/repositories/gosa",
186 help="retrieval base path [%default]", metavar="URL")
187 op.add_option("-a", "--with-smarty", dest="smarty",
188 default=False, action="store_true",
189 help="build smarty3 packages from smarty provided by gosa source")
190 op.add_option("-t", "--target", dest="target",
191 default=".",
192 help="target directory [%default]", metavar="PATH")
193 op.add_option("-m", "--method", dest="method",
194 default="svn",
195 help="retrieval method [%default]", metavar="METHOD")
196 op.add_option("-n", "--no-cleanup", action="store_false",
197 dest="cleanup", default=True,
198 help="don't clean up temporary files")
199 op.add_option("-k", "--key-id", dest="key_id",
200 default=None,
201 help="GPG key ID used for signing if applicable", metavar="KEY_ID")
203 (options, args) = op.parse_args()
204 if len(args) != 1:
205 op.print_help()
206 exit(1)
208 # Allow version shortcut, but prepend tags/
209 release = args[0]
210 if re.match(r"^[0-9]", release):
211 release = "tags/" + release
213 # Validate release
214 if not re.match(r"^(tags/.|branches/.|trunk$)", release):
215 print("Error: release must be in the format tags/x (or just x), branches/x or trunk")
216 exit(1)
218 # Start build
219 build(release, options.source, options.target,
220 options.method, options.key_id, options.cleanup, options.smarty)
223 """ Main GOsa packaging aid """
224 if __name__ == '__main__':
225 if not sys.stdout.encoding:
226 sys.stdout = codecs.getwriter('utf8')(sys.stdout)
227 if not sys.stderr.encoding:
228 sys.stderr = codecs.getwriter('utf8')(sys.stderr)
230 main()