Remove unused code
[bitmask_bundler.git] / bundler / actions.py
1 import datetime
2 import hashlib
3 import os
4 import stat
5 import sys
6 import subprocess
7 import zipfile
8 import urllib
9
10 from abc import ABCMeta, abstractmethod
11 from contextlib import contextmanager
12 from distutils import file_util, dir_util
13
14 from utils import IS_MAC, IS_WIN
15
16 if IS_MAC:
17     from sh import SetFile, hdiutil, codesign
18     from darwin_dyliber import fix_all_dylibs
19 if IS_WIN:
20     import pbs
21     from pbs import cd, glob
22     git = pbs.Command("C:\\Program Files\\Git\\bin\\git.exe")
23     python = pbs.Command("C:\\Python27\\python.exe")
24     pip = pbs.Command("C:\\Python27\\scripts\\pip.exe")
25     mkdir = pbs.Command("C:\\Program Files\\Git\\bin\\mkdir.exe")
26     make = pbs.Command("C:\\MinGW\\bin\\mingw32-make.exe")
27     cp = pbs.Command("C:\\Program Files\\Git\\bin\\cp.exe")
28     rm = pbs.Command("C:\\Program Files\\Git\\bin\\rm.exe")
29     find = pbs.Command("C:\\Program Files\\Git\\bin\\find.exe")
30     ln = pbs.Command("C:\\Program Files\\Git\\bin\\ln.exe")
31     tar = pbs.Command("C:\\Program Files\\Git\\bin\\tar.exe")
32     mv = pbs.Command("C:\\Program Files\\Git\\bin\\mv.exe")
33 else:
34     from sh import git, cd, python, mkdir, make, cp, glob, pip, rm
35     from sh import find, ln, tar, mv
36
37 from depcollector import collect_deps
38
39
40 class Action(object):
41     __metaclass__ = ABCMeta
42
43     def __init__(self, name, basedir, skip=[], do=[]):
44         self._name = name
45         self._basedir = basedir
46         self._skip = skip
47         self._do = do
48
49     @property
50     def name(self):
51         return self._name
52
53     @property
54     def skip(self):
55         return self._name in self._skip
56
57     @property
58     def do(self):
59         if len(self._do) > 0:
60             return self._name in self._do
61         return True
62
63     @abstractmethod
64     def run(self, *args, **kwargs):
65         pass
66
67
68 def skippable(func):
69     def skip_func(self, *args, **kwargs):
70         if self.skip:
71             print "Skipping...", self.name
72             return
73         if not self.do:
74             print "Skipping...", self.name
75             return
76         return func(self, *args, **kwargs)
77     return skip_func
78
79
80 def platform_dir(basedir, *args):
81     dir = os.path.join(basedir,
82                        "Bitmask",
83                        *args)
84     if IS_MAC:
85         dir = os.path.join(basedir,
86                            "Bitmask",
87                            "Bitmask.app",
88                            "Contents",
89                            "MacOS",
90                            *args)
91     return dir
92
93
94 @contextmanager
95 def push_pop(*directories):
96     cd(os.path.join(*directories))
97     yield
98     cd(os.path.join(*(("..",)*len(directories))))
99
100
101 def get_version(repos, nightly):
102     if not nightly:
103         version = "unknown"
104         with push_pop("bitmask_client"):
105             version = git("describe").strip()
106         return version
107
108     m = hashlib.sha256()
109     for repo in repos:
110         version = "unknown"
111         with push_pop(repo):
112             try:
113                 version = git("describe").strip()
114             except:
115                 pass
116         m.update(version)
117
118     return "{0}-{1}".format(str(datetime.date.today()),
119                             m.hexdigest()[:8])
120
121
122 class GitCloneAll(Action):
123     def __init__(self, basedir, skip, do):
124         Action.__init__(self, "gitclone", basedir, skip, do)
125
126     def _repo_url(self, repo_name):
127         if repo_name == "leap_assets":
128             return "git://leap.se/leap_assets"
129         return "git://github.com/leapcode/{0}".format(repo_name)
130
131     @skippable
132     def run(self, sorted_repos, nightly):
133         print "Cloning repositories..."
134         cd(self._basedir)
135         for repo in sorted_repos:
136             print "Cloning", repo
137             rm("-rf", repo)
138             git.clone(self._repo_url(repo), repo)
139             with push_pop(repo):
140                 # Thandy is a special case regarding branches, we'll just use
141                 # develop
142                 if repo in ["thandy", "leap_assets"]:
143                     continue
144                 if not nightly:
145                     git.checkout("master")
146                     git.pull("--ff-only", "origin", "master")
147                     git.fetch()
148                     git.reset("--hard", "origin/master")
149                     latest_tag = git.describe("--abbrev=0").strip()
150                     git.checkout("--quiet", latest_tag)
151                 else:
152                     git.checkout("develop")
153
154         print "Done cloning repos..."
155
156
157 class PythonSetupAll(Action):
158     def __init__(self, basedir, skip, do):
159         Action.__init__(self, "pythonsetup", basedir, skip, do)
160
161     @skippable
162     def run(self, sorted_repos, binaries_path):
163         cd(self._basedir)
164         for repo in sorted_repos:
165             print "Setting up", repo
166             if repo == "soledad":
167                 for subrepo in ["common", "client"]:
168                     with push_pop(repo, subrepo):
169                         pip("install", "-r", "pkg/requirements.pip")
170                         python("setup.py", "develop")
171                         sys.path.append(os.path.join(self._basedir,
172                                                      repo, subrepo, "src"))
173             elif repo in ["bitmask_launcher", "leap_assets"]:
174                 print "Skipping launcher..."
175                 continue
176             else:
177                 with push_pop(repo):
178                     if repo != "thandy":
179                         pip("install", "-r", "pkg/requirements.pip")
180                     else:
181                         # Thandy is a special kid at this point in
182                         # terms of packaging. So we install
183                         # dependencies ourselves for the time being
184                         pip("install", "pycrypto")
185                     if repo == "bitmask_client":
186                         print "Running make on the client..."
187                         make()
188                         print "Running build to get correct version..."
189                         python("setup.py", "build")
190                         print "Updating hashes"
191                         os.environ["OPENVPN_BIN"] = os.path.join(
192                             binaries_path, "openvpn.files", "leap-openvpn")
193                         os.environ["BITMASK_ROOT"] = os.path.join(
194                             self._basedir, repo, "pkg", "linux", "bitmask-root")
195                         python("setup.py", "hash_binaries")
196                     python("setup.py", "develop")
197                     sys.path.append(os.path.join(self._basedir, repo, "src"))
198
199
200 def _convert_path_for_win(path):
201     npath = path
202     if IS_WIN:
203         npath = path.replace("\\", "/")
204     return npath
205
206
207 class CreateDirStructure(Action):
208     def __init__(self, basedir, skip, do):
209         Action.__init__(self, "createdirs", basedir, skip, do)
210
211     @skippable
212     def run(self):
213         print "Creating directory structure..."
214         if IS_MAC:
215             self._darwin_create_dir_structure()
216             self._create_dir_structure(os.path.join(self._basedir,
217                                                     "Bitmask.app",
218                                                     "Contents", "MacOS"))
219         else:
220             self._create_dir_structure(self._basedir)
221         print "Done"
222
223     def _create_dir_structure(self, basedir):
224         mkdirp = mkdir.bake("-p")
225         apps = os.path.join(basedir, "apps")
226         mkdirp(_convert_path_for_win(apps))
227         if IS_WIN:
228             mkdirp(_convert_path_for_win(os.path.join(apps, "eip")))
229         else:
230             mkdirp(_convert_path_for_win(os.path.join(apps, "eip", "files")))
231         mkdirp(_convert_path_for_win(os.path.join(apps, "mail")))
232         mkdirp(_convert_path_for_win(os.path.join(basedir, "lib")))
233
234     def _darwin_create_dir_structure(self):
235         mkdirp = mkdir.bake("-p")
236         app_path = os.path.join(self._basedir, "Bitmask.app")
237         mkdirp(app_path)
238         mkdirp(os.path.join(app_path, "Contents", "MacOS"))
239         mkdirp(os.path.join(app_path, "Contents", "Resources"))
240         mkdirp(os.path.join(app_path, "Contents", "PlugIns"))
241         mkdirp(os.path.join(app_path, "Contents", "StartupItems"))
242         ln("-s", "/Applications", os.path.join(self._basedir, "Applications"))
243
244
245 class CollectAllDeps(Action):
246     def __init__(self, basedir, skip, do):
247         Action.__init__(self, "collectdeps", basedir, skip, do)
248
249     def _remove_unneeded(self, lib_dir):
250         print "Removing unneeded files..."
251         files = find(lib_dir).strip().splitlines()
252         keep = ["QtCore.so",
253                 "QtGui.so",
254                 "__init__.py",
255                 "_utils.py",
256                 "PySide",
257                 ""]  # empty means the whole pyside dir
258         if IS_WIN:
259             keep = ["QtCore4.dll",
260                     "QtGui4.dll",
261                     "__init__.py",
262                     "_utils.py",
263                     "PySide",
264                     "QtGui.pyd",
265                     "QtCore.pyd",
266                     ""]  # empty means the whole pyside dir
267         for f in files:
268             if f.find("PySide") > 0:
269                 if os.path.split(f)[1] not in keep:
270                     rm("-rf", f)
271                     pass
272         print "Done"
273
274     @skippable
275     def run(self, path_file):
276         print "Collecting dependencies..."
277         app_py = os.path.join(self._basedir,
278                               "bitmask_client",
279                               "src",
280                               "leap",
281                               "bitmask",
282                               "app.py")
283         dest_lib_dir = platform_dir(self._basedir, "lib")
284         collect_deps(app_py, dest_lib_dir, path_file)
285
286         self._remove_unneeded(dest_lib_dir)
287         print "Done"
288
289
290 class CopyBinaries(Action):
291     def __init__(self, basedir, skip, do):
292         Action.__init__(self, "copybinaries", basedir, skip, do)
293
294     @skippable
295     def run(self, binaries_path):
296         print "Copying binaries..."
297         dest_lib_dir = platform_dir(self._basedir, "lib")
298
299         if IS_MAC:
300             cp(glob(os.path.join(binaries_path, "Qt*")), dest_lib_dir)
301             cp(glob(os.path.join(binaries_path, "*.dylib")), dest_lib_dir)
302             cp(glob(os.path.join(binaries_path, "Python")), dest_lib_dir)
303             resources_dir = os.path.join(self._basedir,
304                                          "Bitmask",
305                                          "Bitmask.app",
306                                          "Contents",
307                                          "Resources")
308             cp(glob(os.path.join(binaries_path, "openvpn.leap*")),
309                resources_dir)
310
311             mkdir("-p", os.path.join(resources_dir, "openvpn"))
312             cp("-r", glob(os.path.join(binaries_path, "openvpn.files", "*")),
313                os.path.join(resources_dir, "openvpn"))
314
315             cp(os.path.join(binaries_path, "cocoasudo"), resources_dir)
316
317             cp("-r", os.path.join(binaries_path, "qt_menu.nib"), resources_dir)
318             cp("-r", os.path.join(binaries_path, "tuntap-installer.app"),
319                resources_dir)
320             cp(os.path.join(binaries_path, "Bitmask"),
321                platform_dir(self._basedir))
322         elif IS_WIN:
323             root = _convert_path_for_win(
324                 os.path.join(self._basedir, "Bitmask"))
325             for i in glob(os.path.join(binaries_path, "*.dll")):
326                 cp(_convert_path_for_win(i),
327                    root)
328             import win32com
329             win32comext_path = os.path.split(win32com.__file__)[0] + "ext"
330             shell_path = os.path.join(win32comext_path, "shell")
331             cp("-r",
332                _convert_path_for_win(shell_path),
333                _convert_path_for_win(os.path.join(dest_lib_dir, "win32com")))
334             cp(_convert_path_for_win(
335                 os.path.join(binaries_path, "bitmask.exe")),
336                root)
337             cp(_convert_path_for_win(
338                 os.path.join(binaries_path, "Microsoft.VC90.CRT.manifest")),
339                root)
340             cp(_convert_path_for_win(
341                 os.path.join(binaries_path, "openvpn_leap.exe")),
342                _convert_path_for_win(
343                    os.path.join(root, "apps", "eip")))
344             cp(_convert_path_for_win(
345                 os.path.join(binaries_path, "openvpn_leap.exe.manifest")),
346                _convert_path_for_win(
347                    os.path.join(root, "apps", "eip")))
348             cp("-r",
349                _convert_path_for_win(
350                    os.path.join(binaries_path, "tap_driver")),
351                _convert_path_for_win(
352                    os.path.join(root, "apps", "eip")))
353         else:
354             cp(glob(os.path.join(binaries_path, "*.so*")), dest_lib_dir)
355
356             eip_dir = platform_dir(self._basedir, "apps", "eip")
357             #cp(os.path.join(binaries_path, "openvpn"), eip_dir)
358
359             cp("-r", glob(os.path.join(binaries_path, "openvpn.files", "*")),
360                os.path.join(eip_dir, "files"))
361             cp(os.path.join(binaries_path, "bitmask"),
362                platform_dir(self._basedir))
363
364         mail_dir = platform_dir(self._basedir, "apps", "mail")
365         cp(_convert_path_for_win(os.path.join(binaries_path, "gpg")),
366            _convert_path_for_win(mail_dir))
367         print "Done"
368
369
370 class PLister(Action):
371     plist = """<?xml version="1.0" encoding="UTF-8"?>
372 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
373 <plist version="1.0">
374 <dict>
375         <key>CFBundleDisplayName</key>
376         <string>Bitmask</string>
377         <key>CFBundleExecutable</key>
378         <string>MacOS/bitmask-launcher</string>
379         <key>CFBundleIconFile</key>
380         <string>bitmask.icns</string>
381         <key>CFBundleInfoDictionaryVersion</key>
382         <string>6.0</string>
383         <key>CFBundleName</key>
384         <string>Bitmask</string>
385         <key>CFBundlePackageType</key>
386         <string>APPL</string>
387         <key>CFBundleShortVersionString</key>
388         <string>1</string>
389         <key>LSBackgroundOnly</key>
390         <false/>
391         <key>CFBundleIdentifier</key>
392         <string>se.leap.bitmask</string>
393 </dict>
394 </plist>""".split("\n")
395
396     qtconf = """[Paths]
397 Plugins = PlugIns"""
398
399     def __init__(self, basedir, skip, do):
400         Action.__init__(self, "plister", basedir, skip, do)
401
402     @skippable
403     def run(self):
404         print "Generating Info.plist file..."
405         file_util.write_file(os.path.join(self._basedir,
406                                           "Bitmask",
407                                           "Bitmask.app",
408                                           "Contents",
409                                           "Info.plist"),
410                              self.plist)
411         print "Generating qt.conf file..."
412         file_util.write_file(os.path.join(self._basedir,
413                                           "Bitmask",
414                                           "Bitmask.app",
415                                           "Contents",
416                                           "Resources",
417                                           "qt.conf"),
418                              self.qtconf)
419         print "Done"
420
421
422 class SeededConfig(Action):
423     def __init__(self, basedir, skip, do):
424         Action.__init__(self, "seededconfig", basedir, skip, do)
425
426     @skippable
427     def run(self, seeded_config):
428         print "Copying seeded config..."
429         dir_util.copy_tree(seeded_config,
430                            platform_dir(self._basedir, "config"))
431         print "Done"
432
433
434 class DarwinLauncher(Action):
435     launcher = """#!/bin/bash
436 #
437 # Launcher for the LEAP Client under OSX
438 #
439 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)"
440 export DYLD_LIBRARY_PATH=$DIR/lib
441 export PATH=$DIR/../Resources/:$PATH
442 # ---------------------------
443 # DEBUG Info -- enable this if you
444 # are having problems with dynamic libraries loading
445
446 cd "${DIR}" && ./Bitmask $1 $2 $3 $4 $5""".split("\n")
447
448     def __init__(self, basedir, skip, do):
449         Action.__init__(self, "darwinlauncher", basedir, skip, do)
450
451     @skippable
452     def run(self):
453         print "Generating launcher script for OSX..."
454         launcher_path = os.path.join(self._basedir,
455                                      "Bitmask",
456                                      "Bitmask.app",
457                                      "Contents",
458                                      "MacOS",
459                                      "bitmask-launcher")
460         file_util.write_file(launcher_path, self.launcher)
461         os.chmod(launcher_path, stat.S_IRGRP | stat.S_IROTH | stat.S_IRUSR
462                  | stat.S_IWGRP | stat.S_IWOTH | stat.S_IWUSR
463                  | stat.S_IXGRP | stat.S_IXOTH | stat.S_IXUSR)
464         print "Done"
465
466
467 class CopyAssets(Action):
468     def __init__(self, basedir, skip, do):
469         Action.__init__(self, "copyassets", basedir, skip, do)
470
471     @skippable
472     def run(self):
473         print "Copying assets..."
474         resources_dir = os.path.join(self._basedir,
475                                      "Bitmask",
476                                      "Bitmask.app",
477                                      "Contents",
478                                      "Resources")
479         cp(os.path.join(self._basedir, "leap_assets", "mac", "bitmask.icns"),
480            resources_dir)
481         cp(os.path.join(self._basedir, "leap_assets", "mac", "bitmask.tiff"),
482            resources_dir)
483         print "Done"
484
485
486 class CopyMisc(Action):
487     def __init__(self, basedir, skip, do):
488         Action.__init__(self, "copymisc", basedir, skip, do)
489
490     @skippable
491     def run(self):
492         print "Downloading thunderbird extension..."
493         ext_path = platform_dir(self._basedir, "apps",
494                                 "bitmask-thunderbird-latest.xpi")
495         urllib.urlretrieve(
496             "https://downloads.leap.se/thunderbird_extension/"
497             "bitmask-thunderbird-latest.xpi",
498             ext_path)
499         print "Done"
500         print "Copying misc files..."
501         apps_dir = _convert_path_for_win(platform_dir(self._basedir, "apps"))
502         cp(_convert_path_for_win(
503             os.path.join(self._basedir, "bitmask_launcher", "src",
504                          "launcher.py")),
505            apps_dir)
506         cp("-r",
507            _convert_path_for_win(os.path.join(self._basedir, "thandy", "lib",
508                                               "thandy")),
509            apps_dir)
510         cp("-r",
511            _convert_path_for_win(os.path.join(self._basedir, "bitmask_client",
512                                               "src", "leap")),
513            apps_dir)
514         lib_dir = _convert_path_for_win(platform_dir(self._basedir, "lib"))
515         cp(_convert_path_for_win(
516             os.path.join(self._basedir,
517                          "leap_pycommon",
518                          "src", "leap", "common", "cacert.pem")),
519            _convert_path_for_win(os.path.join(lib_dir, "leap", "common")))
520         cp(_convert_path_for_win(glob(os.path.join(self._basedir,
521                                                    "bitmask_client", "build",
522                                                    "lib*", "leap", "bitmask",
523                                                    "_version.py"))[0]),
524            os.path.join(apps_dir, "leap", "bitmask"))
525
526         cp(_convert_path_for_win(
527             os.path.join(self._basedir,
528                          "bitmask_client", "relnotes.txt")),
529            _convert_path_for_win(os.path.join(self._basedir, "Bitmask")))
530         print "Done"
531
532
533 class FixDylibs(Action):
534     def __init__(self, basedir, skip, do):
535         Action.__init__(self, "fixdylibs", basedir, skip, do)
536
537     @skippable
538     def run(self):
539         fix_all_dylibs(platform_dir(self._basedir))
540
541
542 class DmgIt(Action):
543     def __init__(self, basedir, skip, do):
544         Action.__init__(self, "dmgit", basedir, skip, do)
545
546     @skippable
547     def run(self, repos, nightly):
548         print "Dmg'ing it..."
549         cd(self._basedir)
550         version = get_version(repos, nightly)
551         dmg_dir = os.path.join(self._basedir, "dmg")
552         template_dir = os.path.join(self._basedir, "Bitmask")
553         mkdir("-p", dmg_dir)
554         cp("-R", os.path.join(template_dir, "Applications"), dmg_dir)
555         cp("-R", os.path.join(template_dir, "relnotes.txt"), dmg_dir)
556         cp("-R", os.path.join(template_dir, "Bitmask.app"), dmg_dir)
557         cp(os.path.join(self._basedir,
558                         "leap_assets",
559                         "mac", "bitmask.icns"),
560            os.path.join(dmg_dir, ".VolumeIcon.icns"))
561         SetFile("-c", "icnC", os.path.join(dmg_dir, ".VolumeIcon.icns"))
562
563         vol_name = "Bitmask"
564         dmg_name = "Bitmask-OSX-{0}.dmg".format(version)
565         raw_dmg_path = os.path.join(self._basedir, "raw-{0}".format(dmg_name))
566         dmg_path = os.path.join(self._basedir, dmg_name)
567
568         hdiutil("create", "-srcfolder", dmg_dir, "-volname", vol_name,
569                 "-fsargs", "-c c=64,a=16,e=16", "-fs", "HFS+",
570                 "-format", "UDRW", "-ov", "-size", "500000k",
571                 raw_dmg_path)
572         rm("-rf", dmg_dir)
573         mkdir(dmg_dir)
574         hdiutil("attach", raw_dmg_path, "-mountpoint", dmg_dir)
575         SetFile("-a", "C", dmg_dir)
576         hdiutil("detach", dmg_dir)
577
578         rm("-rf", dmg_dir)
579         hdiutil("convert", raw_dmg_path, "-format", "UDZO",
580                 "-imagekey", "zlib-level=9", "-o",
581                 dmg_path)
582         rm("-f", raw_dmg_path)
583         print "Done"
584
585
586 class TarballIt(Action):
587     def __init__(self, basedir, skip, do):
588         Action.__init__(self, "tarballit", basedir, skip, do)
589
590     @skippable
591     def run(self, repos, nightly):
592         print "Tarballing it..."
593         cd(self._basedir)
594         version = get_version(repos, nightly)
595         import platform
596         bits = platform.architecture()[0][:2]
597         bundle_name = "Bitmask-linux%s-%s" % (bits, version)
598         mv("Bitmask", bundle_name)
599         tar("cjf", bundle_name+".tar.bz2", bundle_name)
600         print "Done"
601
602
603 class PycRemover(Action):
604     def __init__(self, basedir, skip, do):
605         Action.__init__(self, "removepyc", basedir, skip, do)
606
607     @skippable
608     def run(self):
609         print "Removing .pyc files..."
610         files = find(self._basedir, "-name", "*.pyc").strip().splitlines()
611         for f in files:
612             rm(f)
613         print "Done"
614
615
616 class MtEmAll(Action):
617     def __init__(self, basedir, skip, do):
618         Action.__init__(self, "mtemall", basedir, skip, do)
619
620     @skippable
621     def run(self):
622         print "Mt'ing all the files..."
623         cd(os.path.join(self._basedir, "Bitmask"))
624         subprocess.check_call(
625             ["C:\\Program Files\\Windows Kits\\8.0\\bin\\x86\\mt.exe",
626              "-nologo", "-manifest", "Microsoft.VC90.CRT.manifest",
627              "-outputresource:bitmask.exe;#1"])
628         cd(os.path.join("apps", "eip"))
629         subprocess.check_call(
630             ["C:\\Program Files\\Windows Kits\\8.0\\bin\\x86\\mt.exe",
631              "-nologo", "-manifest", "openvpn_leap.exe.manifest",
632              "-outputresource:openvpn_leap.exe;#1"])
633         print "Done"
634
635
636 class ZipIt(Action):
637     def __init__(self, basedir, skip, do):
638         Action.__init__(self, "zipit", basedir, skip, do)
639
640     def _zipdir(self, path, zf):
641         for root, dirs, files in os.walk(path):
642             for f in files:
643                 zf.write(os.path.join(root, f))
644
645     @skippable
646     def run(self, repos, nightly):
647         print "Ziping it..."
648         cd(self._basedir)
649         version = get_version(repos, nightly)
650         name = "Bitmask-win32-{0}".format(version)
651         mv(_convert_path_for_win(os.path.join(self._basedir, "Bitmask")),
652            _convert_path_for_win(os.path.join(self._basedir, name)))
653         zf = zipfile.ZipFile("{0}.zip".format(name), "w", zipfile.ZIP_DEFLATED)
654         self._zipdir(name, zf)
655         zf.close()
656         print "Done"
657
658
659 class SignIt(Action):
660     def __init__(self, basedir, skip, do):
661         Action.__init__(self, "signit", basedir, skip, do)
662
663     @skippable
664     def run(self, identity):
665         print "Signing tuntap kext..."
666         kext = os.path.join(self._basedir,
667                             "Bitmask",
668                             "Bitmask.app",
669                             "Contents",
670                             "Resources",
671                             "tuntap-installer.app",
672                             "Contents",
673                             "Extensions",
674                             "tun.kext")
675         codesign("-s", identity, "--deep", kext)
676         print "Done"
677         print "Signing tuntap installer..."
678         tuntap_app = os.path.join(self._basedir,
679                                   "Bitmask",
680                                   "Bitmask.app",
681                                   "Contents",
682                                   "Resources",
683                                   "tuntap-installer.app")
684         codesign("-s", identity, "--deep", tuntap_app)
685         print "Done"
686         print "Signing main structure, this will take a while..."
687         main_app = os.path.join(self._basedir,
688                                 "Bitmask",
689                                 "Bitmask.app")
690         print codesign("-s", identity, "--force", "--deep", "--verbose", main_app)
691         print "Done"
692
693
694
695 class RemoveUnused(Action):
696     def __init__(self, basedir, skip, do):
697         Action.__init__(self, "rmunused", basedir, skip, do)
698
699     @skippable
700     def run(self):
701         print "Removing unused python code..."
702         test_dirs = find(self._basedir, "-name", "*test*").strip().splitlines()
703         for td in test_dirs:
704             rm("-rf", os.path.join(self._basedir, td))
705
706         twisted_used = ["aplication", "conch", "cred", "version", "internet", "mail"]
707         # twisted_files = find(self._basedir, "-name", "t
708         print "Done"
709