Merge lp:~brian.curtin/ubuntuone-windows-installer/build_installer into lp:ubuntuone-windows-installer

Proposed by Brian Curtin
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 119
Merged at revision: 114
Proposed branch: lp:~brian.curtin/ubuntuone-windows-installer/build_installer
Merge into: lp:ubuntuone-windows-installer
Diff against target: 211 lines (+129/-31)
3 files modified
scripts/build_installer.py (+99/-0)
scripts/setup.py (+30/-0)
scripts/ubuntuone.xml (+0/-31)
To merge this branch: bzr merge lp:~brian.curtin/ubuntuone-windows-installer/build_installer
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Roberto Alsina (community) Approve
Review via email: mp+105403@code.launchpad.net

Commit message

- Automate the building and packaging of the Windows installer

Description of the change

This branch adds build_installer.py which fully automates the creation of the BitRock installer. It includes the typical setup.py steps of fetch, prepare, and py2exe, then it runs the BitRock autoupdate and installer creation. Upon creation of the installer, it timestamps the installer name so we can identify which installers were created by the automation on which date.

Also included in the branch are changes which make the automation easier. The VistaLib DLLs are downloaded from my U1 rather than forcing a user to create an account on CodeProject, sign in, then download the zip file from there. The files are placed directly into dist/ where they are needed, along with the required license file.

A change was made to the setup.py file to package the CRT alongside the binaries rather than requiring the vcredist installer to be packaged in the installer and run. It makes the installer a bit quicker and it makes automation much easier.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1 code review

review: Approve
Revision history for this message
Manuel de la Peña (mandel) wrote :

Here are some comments about the branch:

1. Imports are not in order, I personally don't care but I do know that members of the team like them to be in alphabetical order etc... can you please set them that way (I know, I know...)

2. _find_bitrock is a little fragile. The method is assuming that the package is going to be build in a x64 machine which might not be the case (I do not know the arch of the ec2 instance that runs jenkins). I think that a more 'robust' way to find bitrock will be looking in the registry. After installation I've noticed that you can find the installation path under HKEY_LOCAL_MACHINE\Software\BitRock\BitRock InstallBuilder Enterprise\Locatin (same path + the Wow6432Node in x64 machines). I think it is better approach over looking at the ProgramFiles(x86) path.

The rest of the code looks great to me (I'm really looking foward to add a +1 to this and get automated builds in jenkins).

review: Needs Fixing
117. By Brian Curtin

Turn renaming off because Jenkins doesn't need a more unique path, just a predictable one. Removing the rename option also makes it easier to upload the resulting installer to a beta channel or something like that.

118. By Brian Curtin

Reorder imports

119. By Brian Curtin

Use the registry to determine BitRock's installation path rather than trying to discover it

Revision history for this message
Manuel de la Peña (mandel) wrote :

Superb!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'scripts/build_installer.py'
2--- scripts/build_installer.py 1970-01-01 00:00:00 +0000
3+++ scripts/build_installer.py 2012-05-17 15:00:27 +0000
4@@ -0,0 +1,99 @@
5+from __future__ import print_function
6+from collections import namedtuple
7+import os
8+import shutil
9+import subprocess
10+import sys
11+import time
12+
13+try:
14+ import _winreg as winreg
15+except ImportError:
16+ import winreg
17+
18+def _which_python():
19+ for dir in os.environ.get("Path").split(os.pathsep):
20+ full_path = os.path.join(dir, "python.exe")
21+ if os.path.exists(full_path):
22+ return full_path
23+ return None
24+
25+BRCommand = namedtuple("BRCommand", ["bin", "args", "output"])
26+
27+# Since buildout's python.exe is really a script passed to the
28+# system-installed Python, we need to get that same environment by starting
29+# Python in the same manner.
30+# Ex. ['c:\\Python27\\python.exe', 'C:\\u1\\bin\\python-script.py']
31+PYTHON = [_which_python()]
32+
33+def _find_bitrock():
34+ with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
35+ r"Software\BitRock\BitRock InstallBuilder Professional", 0,
36+ winreg.KEY_READ | winreg.KEY_WOW64_32KEY) as key:
37+ path, reg_type = winreg.QueryValueEx(key, "Location")
38+ if reg_type != winreg.REG_SZ:
39+ raise Exception(
40+ "Got unexpected Location information: {}".format(reg_type))
41+ return path if os.path.exists(path) else None
42+
43+def _find_output_dir(type):
44+ user_dir = os.getenv("USERPROFILE")
45+ if not user_dir:
46+ return None
47+ output = os.path.join(user_dir, "Documents", type, "output")
48+ return output if os.path.exists(output) else None
49+
50+def _get_last_file(dir):
51+ """Get the last written file in this directory. We just wrote it."""
52+ files = os.listdir(dir)
53+ # Sort the files by modification time.
54+ files.sort(key=lambda f: os.stat(os.path.join(dir, f)).st_mtime)
55+ # We want the most recently modified, so take the last one.
56+ return os.path.join(dir, files[-1])
57+
58+def setup_command(cmd):
59+ """Run a command through the setup.py script"""
60+ print("Calling", cmd)
61+ rv = subprocess.Popen(PYTHON + ["setup.py", cmd]).wait()
62+ if rv != 0:
63+ raise Exception("{} returned {}".format(cmd, rv))
64+ print("{} returned {}".format(cmd, rv))
65+
66+def bitrock_command(cmd, rename=False):
67+ """Run one of the BitRock binaries with some arguments"""
68+ print("Running BitRock", cmd.bin)
69+ subprocess.check_output(
70+ [os.path.join(_find_bitrock(), cmd.bin)] + cmd.args,
71+ stderr=subprocess.STDOUT)
72+
73+ source = _get_last_file(cmd.output)
74+ target = os.path.join(os.path.dirname(__file__) or os.getcwd(),
75+ os.path.basename(source))
76+
77+ # Copy the installer locally
78+ shutil.copy(source, target)
79+ if rename:
80+ orig_target = target
81+ # Rename the installer to be timestamped.
82+ root, ext = os.path.splitext(target)
83+ timestamp = time.strftime("%Y%m%d-%H%M%S", time.gmtime())
84+ target = "{}-{}{}".format(root, timestamp, ext)
85+ os.rename(orig_target, target)
86+ print("Created", target)
87+
88+def main():
89+ for cmd in ["fetch", "prepare", "py2exe"]:
90+ setup_command(cmd)
91+
92+ autoupdate = BRCommand(os.path.join("autoupdate", "bin", "customize.exe"),
93+ ["build", "ubuntuone_autoupdate.xml", "windows"],
94+ _find_output_dir("AutoUpdate"))
95+ installer = BRCommand(os.path.join("bin", "builder-cli.exe"),
96+ ["build", "ubuntuone.xml"],
97+ _find_output_dir("InstallBuilder"))
98+ for cmd, rename in ((autoupdate, False), (installer, False)):
99+ bitrock_command(cmd, rename)
100+
101+if __name__ == "__main__":
102+ main()
103+
104
105=== modified file 'scripts/setup.py'
106--- scripts/setup.py 2012-03-23 21:02:45 +0000
107+++ scripts/setup.py 2012-05-17 15:00:27 +0000
108@@ -20,10 +20,12 @@
109 import shutil
110 import subprocess
111 import sys
112+import urllib
113
114 from distutils import log
115 from distutils.cmd import Command
116 from distutils.core import setup
117+from glob import glob
118
119 import conf
120
121@@ -330,6 +332,33 @@
122 console.extend(windows)
123 windows = []
124
125+ # From http://www.py2exe.org/index.cgi/Tutorial#Step521
126+ # Bundle our own copy of the CRT so we don't need to depend on the
127+ # vcredist_x86.exe for automation, or as a step of installer.
128+ data_files = [("Microsoft.VC90.CRT",
129+ glob(r"C:\Program Files (x86)"
130+ r"\Microsoft Visual Studio 9.0\vc\redist"
131+ r"\x86\Microsoft.VC90.CRT\*.*"))]
132+
133+ if not os.path.exists("dist"):
134+ os.mkdir("dist")
135+
136+ # The VistaLib license requires that we distribute this file.
137+ shutil.copy("README_nonelevated.txt", "dist")
138+
139+ vistalib32 = "http://ubuntuone.com/2jcKAeBwIw8joW8mEu5JYX"
140+ vistalib64 = "http://ubuntuone.com/16h3eDF4NPCLIUFJeBYxxc"
141+
142+ for url, name in ((vistalib32, "VistaLib32.dll"),
143+ (vistalib64, "VistaLib64.dll")):
144+ response = urllib.urlopen(url)
145+ data = response.read()
146+ if not data:
147+ raise Exception("Unable to download %s" % URL)
148+
149+ with open(os.path.join("dist", name), "wb") as f:
150+ f.write(data)
151+
152 # Build bundles
153 setup(
154 name='ubuntuone',
155@@ -346,6 +375,7 @@
156 },
157 console=console,
158 windows=windows,
159+ data_files=data_files,
160 options={
161 'py2exe': {
162 'includes': ['google.protobuf.descriptor',
163
164=== modified file 'scripts/ubuntuone.xml'
165--- scripts/ubuntuone.xml 2012-04-05 19:45:42 +0000
166+++ scripts/ubuntuone.xml 2012-05-17 15:00:27 +0000
167@@ -94,9 +94,6 @@
168 <origin>data</origin>
169 </distributionDirectory>
170 <distributionFile>
171- <origin>vcredist_x86.exe</origin>
172- </distributionFile>
173- <distributionFile>
174 <origin>ubuntu_one.ico</origin>
175 </distributionFile>
176 </distributionFileList>
177@@ -151,34 +148,6 @@
178 </startMenuFileShortcut>
179 </startMenuShortcutList>
180 </component>
181- <component>
182- <name>vcruntime</name>
183- <canBeEdited>1</canBeEdited>
184- <selected>1</selected>
185- <show>1</show>
186- <folderList>
187- <folder>
188- <destination>${installdir}</destination>
189- <name>vcfiles</name>
190- <platforms>all</platforms>
191- <actionList>
192- <runProgram>
193- <abortOnError>0</abortOnError>
194- <program>${installdir}/vcredist_x86.exe</program>
195- <programArguments>/q
196-</programArguments>
197- <progressText>Installing C++ runtime</progressText>
198- <showMessageOnError>0</showMessageOnError>
199- </runProgram>
200- </actionList>
201- <distributionFileList>
202- <distributionFile>
203- <origin>vcredist_x86.exe</origin>
204- </distributionFile>
205- </distributionFileList>
206- </folder>
207- </folderList>
208- </component>
209 </componentList>
210 <initializationActionList>
211 <setInstallerVariable name="msiexec" value=""/>

Subscribers

People subscribed via source and target branches