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