chiark / gitweb /
wrapper.fhtml: Put `html' in lowercase in the DOCTYPE declaration.
[chopwood] / agpl.py
... / ...
CommitLineData
1### -*-python-*-
2###
3### GNU Affero General Public License compliance
4###
5### (c) 2013 Mark Wooding
6###
7
8###----- Licensing notice ---------------------------------------------------
9###
10### This file is part of Chopwood: a password-changing service.
11###
12### Chopwood is free software; you can redistribute it and/or modify
13### it under the terms of the GNU Affero General Public License as
14### published by the Free Software Foundation; either version 3 of the
15### License, or (at your option) any later version.
16###
17### Chopwood is distributed in the hope that it will be useful,
18### but WITHOUT ANY WARRANTY; without even the implied warranty of
19### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20### GNU Affero General Public License for more details.
21###
22### You should have received a copy of the GNU Affero General Public
23### License along with Chopwood; if not, see
24### <http://www.gnu.org/licenses/>.
25
26import contextlib as CTX
27import grp as GR
28import os as OS
29import pwd as PW
30import shlex as SL
31import shutil as SH
32import subprocess as SUB
33import sys as SYS
34import tarfile as TAR
35import tempfile as TF
36import time as T
37
38from cStringIO import StringIO
39
40from auto import PACKAGE, VERSION
41import util as U
42
43@CTX.contextmanager
44def tempdir():
45 d = TF.mkdtemp()
46 try: yield d
47 finally: SH.rmtree(d, ignore_errors = True)
48
49def dirs_to_dump():
50 dirs = set()
51 for m in SYS.modules.itervalues():
52 try: f = m.__file__
53 except AttributeError: continue
54 d = OS.path.realpath(OS.path.dirname(f))
55 if d.startswith('/usr/') and not d.startswith('/usr/local/'): continue
56 dirs.add(d)
57 dirs = sorted(dirs)
58 last = '!'
59 dump = []
60 for d in dirs:
61 if d.startswith(last): continue
62 dump.append(d)
63 last = d
64 return dump
65
66def exists_subdir(subdir):
67 return lambda dir: OS.path.isdir(OS.path.join(dir, subdir))
68
69def filez(cmd):
70 def _(dir):
71 kid = SUB.Popen(SL.split(cmd), stdout = SUB.PIPE, cwd = dir)
72 left = ''
73 while True:
74 buf = kid.stdout.read(16384)
75 if not buf: break
76 buf = left + buf
77 i = 0
78 while True:
79 z = buf.find('\0', i)
80 if z < 0: break
81 f = buf[i:z]
82 i = z + 1
83 if f == '.': continue
84 if f.startswith('./'): f = f[2:]
85 yield f
86 left = buf[i:]
87 if left:
88 raise U.ExpectedError, \
89 (500, "trailing junk from `%s' in `%s'" % (cmd, dir))
90 return _
91
92DUMPERS = [
93 (exists_subdir('.git'), [filez('git ls-files -coz --exclude-standard'),
94 filez('find .git -print0')]),
95 (lambda d: True, [filez('find . ( ! -perm +004 -prune ) -o -print0')])]
96
97def dump_dir(name, dir, dirmap, tf, root):
98 for test, listers in DUMPERS:
99 if test(dir): break
100 else:
101 raise U.ExpectedError, (500, "no dumper for `%s'" % dir)
102 tf.add(dir, OS.path.join(root, name), recursive = False)
103 for lister in listers:
104 for file in lister(dir):
105 full = OS.path.join(dir, file)
106 tarname = OS.path.join(root, name, file)
107 skip = False
108 if OS.path.islink(full):
109 dest = OS.path.realpath(full)
110 for d, local in dirmap:
111 if dest.startswith(d):
112 fix = OS.path.relpath(OS.path.join('/', local, dest[len(d):]),
113 OS.path.join('/', name,
114 OS.path.dirname(file)))
115 st = OS.stat(full)
116 ti = tf.gettarinfo(full, tarname)
117 ti.linkname = fix
118 tf.addfile(ti)
119 skip = True
120 if not skip:
121 tf.add(full, tarname, recursive = False)
122
123def source(out):
124 if SYS.version_info >= (2, 6):
125 tf = TAR.open(fileobj = out, mode = 'w|gz', format = TAR.USTAR_FORMAT)
126 else:
127 tf = TAR.open(fileobj = out, mode = 'w|gz')
128 tf.posix = True
129 root = '%s-%s' % (PACKAGE, VERSION)
130 seen = set()
131 dirmap = []
132 festout = StringIO()
133 for dir in dirs_to_dump():
134 dir = dir.rstrip('/')
135 base = OS.path.basename(dir)
136 if base not in seen:
137 name = base
138 else:
139 for i in I.count():
140 name = '%s.%d' % (base, i)
141 if name not in seen: break
142 dirmap.append((dir + '/', name))
143 festout.write('%s = %s\n' % (name, dir))
144 fest = festout.getvalue()
145 ti = TAR.TarInfo(OS.path.join(root, 'MANIFEST'))
146 ti.size = len(fest)
147 ti.mtime = T.time()
148 ti.mode = 0664
149 ti.type = TAR.REGTYPE
150 uid = OS.getuid(); ti.uid, ti.uname = uid, PW.getpwuid(uid).pw_name
151 gid = OS.getgid(); ti.gid, ti.gname = gid, GR.getgrgid(gid).gr_name
152 tf.addfile(ti, fileobj = StringIO(fest))
153 for dir, name in dirmap:
154 dump_dir(name, dir, dirmap, tf, root)
155 tf.close()
156
157###----- That's all, folks --------------------------------------------------