Status: | Merged |
---|---|
Approved by: | Michael Vogt |
Approved revision: | 557 |
Merged at revision: | 452 |
Proposed branch: | lp:click/devel |
Merge into: | lp:click |
Diff against target: |
747 lines (+318/-153) 10 files modified
click/build.py (+4/-1) click/chroot.py (+174/-128) click/commands/install.py (+5/-1) click/install.py (+17/-6) click/tests/test_build.py (+2/-0) click/tests/test_chroot.py (+14/-5) click/tests/test_install.py (+5/-11) debian/changelog (+30/-0) debian/click.postinst (+3/-1) lib/click/user.vala (+64/-0) |
To merge this branch: | bzr merge lp:click/devel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Vogt | Approve | ||
Review via email: mp+249631@code.launchpad.net |
Commit message
Click 0.4.37 (skipping 0.4.36 to workaround version conflicts in PPAs): bugfixes, geoip based chroot mirror selection
Description of the change
[ Michael Vogt ]
* lp:~mvo/click/no-error-no-missing-systemctl:
- fix a spurious error message on systems without systemctl
* lp:~mvo/click/do-not-crash-in-build-on-broken-symlinks:
- do not crash when building a click package that contains broken
symlinks
* lp:~mvo/click/dpkg-less-verbose:
- do not show dpkg output on install unless --verbose is used
* lp:~mvo/click/lp1394256-run-user-hooks:
- ensures that click user hooks are run for all logged in users when
click is used with "--all-users".
* lp:~mvo/click/qt5-qmake-cross-armhf:
- add qt5-qmake-
* lp:~mvo/click/chroot-15.04-multiarch:
- add ubuntu-
* lp:~mvo/click/lp1394256-run-user-hooks-on-remove-too:
- Run the click remove user hooks for all logged in users.
* click/chroot.py:
- use string.format() for chroot TARGET selection
* skip 0.4.36 version and go straight to 0.4.37 (LP: #1418086)
[ Zoltan Balogh ]
* lp:~bzoltan/click/vivid-transition_mirrors:
- use geoip to guess the most suitable country mirror when creating
the click chroot
Michael Vogt (mvo) : | # |
Preview Diff
1 | === modified file 'click/build.py' |
2 | --- click/build.py 2014-05-05 13:10:19 +0000 |
3 | +++ click/build.py 2015-02-13 12:37:00 +0000 |
4 | @@ -274,7 +274,10 @@ |
5 | with open(md5sums_path, "w") as md5sums: |
6 | for path in sorted(self.list_files(root_path)): |
7 | md5 = hashlib.md5() |
8 | - with open(os.path.join(root_path, path), "rb") as f: |
9 | + p = os.path.join(root_path, path) |
10 | + if not os.path.exists(p): |
11 | + continue |
12 | + with open(p, "rb") as f: |
13 | while True: |
14 | buf = f.read(16384) |
15 | if not buf: |
16 | |
17 | === modified file 'click/chroot.py' |
18 | --- click/chroot.py 2014-11-13 10:20:37 +0000 |
19 | +++ click/chroot.py 2015-02-13 12:37:00 +0000 |
20 | @@ -26,6 +26,8 @@ |
21 | "ClickChrootDoesNotExistException", |
22 | ] |
23 | |
24 | +import urllib |
25 | +import urllib.request |
26 | import os |
27 | import pwd |
28 | import re |
29 | @@ -34,6 +36,7 @@ |
30 | import subprocess |
31 | import sys |
32 | from textwrap import dedent |
33 | +from xml.etree import ElementTree |
34 | |
35 | |
36 | framework_base = { |
37 | @@ -67,114 +70,121 @@ |
38 | # Please keep the lists of package names sorted. |
39 | extra_packages = { |
40 | "ubuntu-sdk-13.10": [ |
41 | - "libqt5opengl5-dev:TARGET", |
42 | - "libqt5svg5-dev:TARGET", |
43 | - "libqt5v8-5-dev:TARGET", |
44 | - "libqt5webkit5-dev:TARGET", |
45 | - "libqt5xmlpatterns5-dev:TARGET", |
46 | - "qmlscene:TARGET", |
47 | - "qt3d5-dev:TARGET", |
48 | - "qt5-default:TARGET", |
49 | - "qt5-qmake:TARGET", |
50 | - "qtbase5-dev:TARGET", |
51 | - "qtdeclarative5-dev:TARGET", |
52 | - "qtmultimedia5-dev:TARGET", |
53 | - "qtquick1-5-dev:TARGET", |
54 | - "qtscript5-dev:TARGET", |
55 | - "qtsensors5-dev:TARGET", |
56 | - "qttools5-dev:TARGET", |
57 | + "libqt5opengl5-dev:{TARGET}", |
58 | + "libqt5svg5-dev:{TARGET}", |
59 | + "libqt5v8-5-dev:{TARGET}", |
60 | + "libqt5webkit5-dev:{TARGET}", |
61 | + "libqt5xmlpatterns5-dev:{TARGET}", |
62 | + "qmlscene:{TARGET}", |
63 | + "qt3d5-dev:{TARGET}", |
64 | + "qt5-default:{TARGET}", |
65 | + "qt5-qmake:{TARGET}", |
66 | + "qtbase5-dev:{TARGET}", |
67 | + "qtdeclarative5-dev:{TARGET}", |
68 | + "qtmultimedia5-dev:{TARGET}", |
69 | + "qtquick1-5-dev:{TARGET}", |
70 | + "qtscript5-dev:{TARGET}", |
71 | + "qtsensors5-dev:{TARGET}", |
72 | + "qttools5-dev:{TARGET}", |
73 | "ubuntu-ui-toolkit-doc", |
74 | ], |
75 | "ubuntu-sdk-14.04": [ |
76 | "cmake", |
77 | - "google-mock:TARGET", |
78 | + "google-mock:{TARGET}", |
79 | "intltool", |
80 | - "libboost1.54-dev:TARGET", |
81 | - "libjsoncpp-dev:TARGET", |
82 | - "libprocess-cpp-dev:TARGET", |
83 | - "libproperties-cpp-dev:TARGET", |
84 | - "libqt5svg5-dev:TARGET", |
85 | - "libqt5webkit5-dev:TARGET", |
86 | - "libqt5xmlpatterns5-dev:TARGET", |
87 | - "libunity-scopes-dev:TARGET", |
88 | + "libboost1.54-dev:{TARGET}", |
89 | + "libjsoncpp-dev:{TARGET}", |
90 | + "libprocess-cpp-dev:{TARGET}", |
91 | + "libproperties-cpp-dev:{TARGET}", |
92 | + "libqt5svg5-dev:{TARGET}", |
93 | + "libqt5webkit5-dev:{TARGET}", |
94 | + "libqt5xmlpatterns5-dev:{TARGET}", |
95 | + "libunity-scopes-dev:{TARGET}", |
96 | # bug #1316930, needed for autopilot |
97 | "python3", |
98 | - "qmlscene:TARGET", |
99 | - "qt3d5-dev:TARGET", |
100 | - "qt5-default:TARGET", |
101 | - "qtbase5-dev:TARGET", |
102 | - "qtdeclarative5-dev:TARGET", |
103 | + "qmlscene:{TARGET}", |
104 | + "qt3d5-dev:{TARGET}", |
105 | + "qt5-default:{TARGET}", |
106 | + "qtbase5-dev:{TARGET}", |
107 | + "qtdeclarative5-dev:{TARGET}", |
108 | "qtdeclarative5-dev-tools", |
109 | - "qtlocation5-dev:TARGET", |
110 | - "qtmultimedia5-dev:TARGET", |
111 | - "qtscript5-dev:TARGET", |
112 | - "qtsensors5-dev:TARGET", |
113 | - "qttools5-dev:TARGET", |
114 | - "qttools5-dev-tools:TARGET", |
115 | + "qtlocation5-dev:{TARGET}", |
116 | + "qtmultimedia5-dev:{TARGET}", |
117 | + "qtscript5-dev:{TARGET}", |
118 | + "qtsensors5-dev:{TARGET}", |
119 | + "qttools5-dev:{TARGET}", |
120 | + "qttools5-dev-tools:{TARGET}", |
121 | "ubuntu-ui-toolkit-doc", |
122 | ], |
123 | "ubuntu-sdk-14.10": [ |
124 | "cmake", |
125 | "cmake-extras", |
126 | - "google-mock:TARGET", |
127 | + "google-mock:{TARGET}", |
128 | "intltool", |
129 | - "libboost1.55-dev:TARGET", |
130 | - "libcontent-hub-dev:TARGET", |
131 | - "libjsoncpp-dev:TARGET", |
132 | - "libnet-cpp-dev:TARGET", |
133 | - "libprocess-cpp-dev:TARGET", |
134 | - "libproperties-cpp-dev:TARGET", |
135 | - "libqt5keychain0:TARGET", |
136 | - "libqt5sensors5-dev:TARGET", |
137 | - "libqt5svg5-dev:TARGET", |
138 | - "libqt5webkit5-dev:TARGET", |
139 | - "libqt5xmlpatterns5-dev:TARGET", |
140 | - "libunity-scopes-dev:TARGET", |
141 | + "libboost1.55-dev:{TARGET}", |
142 | + "libcontent-hub-dev:{TARGET}", |
143 | + "libjsoncpp-dev:{TARGET}", |
144 | + "libnet-cpp-dev:{TARGET}", |
145 | + "libprocess-cpp-dev:{TARGET}", |
146 | + "libproperties-cpp-dev:{TARGET}", |
147 | + "libqt5keychain0:{TARGET}", |
148 | + "libqt5sensors5-dev:{TARGET}", |
149 | + "libqt5svg5-dev:{TARGET}", |
150 | + "libqt5webkit5-dev:{TARGET}", |
151 | + "libqt5xmlpatterns5-dev:{TARGET}", |
152 | + "libunity-scopes-dev:{TARGET}", |
153 | # bug #1316930, needed for autopilot |
154 | "python3", |
155 | - "qml-module-qt-labs-settings:TARGET", |
156 | - "qml-module-qtmultimedia:TARGET", |
157 | - "qml-module-qtquick-layouts:TARGET", |
158 | - "qml-module-qtsensors:TARGET", |
159 | - "qml-module-qtwebkit:TARGET", |
160 | - "qmlscene:TARGET", |
161 | - "qt3d5-dev:TARGET", |
162 | - "qt5-default:TARGET", |
163 | - "qtdeclarative5-accounts-plugin:TARGET", |
164 | + "qml-module-qt-labs-settings:{TARGET}", |
165 | + "qml-module-qtmultimedia:{TARGET}", |
166 | + "qml-module-qtquick-layouts:{TARGET}", |
167 | + "qml-module-qtsensors:{TARGET}", |
168 | + "qml-module-qtwebkit:{TARGET}", |
169 | + "qmlscene:{TARGET}", |
170 | + "qt3d5-dev:{TARGET}", |
171 | + "qt5-default:{TARGET}", |
172 | + "qtdeclarative5-accounts-plugin:{TARGET}", |
173 | "qtdeclarative5-dev-tools", |
174 | - "qtdeclarative5-folderlistmodel-plugin:TARGET", |
175 | - "qtdeclarative5-localstorage-plugin:TARGET", |
176 | - "qtdeclarative5-online-accounts-client0.1:TARGET", |
177 | - "qtdeclarative5-particles-plugin:TARGET", |
178 | - "qtdeclarative5-poppler1.0:TARGET", |
179 | - "qtdeclarative5-qtlocation-plugin:TARGET", |
180 | - "qtdeclarative5-qtorganizer-plugin:TARGET", |
181 | - "qtdeclarative5-qtpositioning-plugin:TARGET", |
182 | - "qtdeclarative5-u1db1.0:TARGET", |
183 | - "qtdeclarative5-ubuntu-content0.1:TARGET", |
184 | - "qtdeclarative5-ubuntu-download-manager0.1:TARGET", |
185 | - "qtdeclarative5-ubuntu-mediascanner0.1:TARGET", |
186 | - "qtdeclarative5-ubuntu-syncmonitor0.1:TARGET", |
187 | - "qtdeclarative5-ubuntu-telephony-phonenumber0.1:TARGET", |
188 | - "qtdeclarative5-ubuntu-ui-toolkit-plugin:TARGET", |
189 | - "qtdeclarative5-usermetrics0.1:TARGET", |
190 | - "qtdeclarative5-xmllistmodel-plugin:TARGET", |
191 | - "qtlocation5-dev:TARGET", |
192 | - "qtmultimedia5-dev:TARGET", |
193 | - "qtscript5-dev:TARGET", |
194 | - "qttools5-dev:TARGET", |
195 | - "qttools5-dev-tools:TARGET", |
196 | - "ubuntu-html5-theme:TARGET", |
197 | + "qtdeclarative5-folderlistmodel-plugin:{TARGET}", |
198 | + "qtdeclarative5-localstorage-plugin:{TARGET}", |
199 | + "qtdeclarative5-online-accounts-client0.1:{TARGET}", |
200 | + "qtdeclarative5-particles-plugin:{TARGET}", |
201 | + "qtdeclarative5-poppler1.0:{TARGET}", |
202 | + "qtdeclarative5-qtlocation-plugin:{TARGET}", |
203 | + "qtdeclarative5-qtorganizer-plugin:{TARGET}", |
204 | + "qtdeclarative5-qtpositioning-plugin:{TARGET}", |
205 | + "qtdeclarative5-u1db1.0:{TARGET}", |
206 | + "qtdeclarative5-ubuntu-content0.1:{TARGET}", |
207 | + "qtdeclarative5-ubuntu-download-manager0.1:{TARGET}", |
208 | + "qtdeclarative5-ubuntu-mediascanner0.1:{TARGET}", |
209 | + "qtdeclarative5-ubuntu-syncmonitor0.1:{TARGET}", |
210 | + "qtdeclarative5-ubuntu-telephony-phonenumber0.1:{TARGET}", |
211 | + "qtdeclarative5-ubuntu-ui-toolkit-plugin:{TARGET}", |
212 | + "qtdeclarative5-usermetrics0.1:{TARGET}", |
213 | + "qtdeclarative5-xmllistmodel-plugin:{TARGET}", |
214 | + "qtlocation5-dev:{TARGET}", |
215 | + "qtmultimedia5-dev:{TARGET}", |
216 | + "qtscript5-dev:{TARGET}", |
217 | + "qttools5-dev:{TARGET}", |
218 | + "qttools5-dev-tools:{TARGET}", |
219 | + "ubuntu-html5-theme:{TARGET}", |
220 | "ubuntu-ui-toolkit-doc", |
221 | ], |
222 | "ubuntu-sdk-15.04": [ |
223 | # the sdk libs |
224 | - "ubuntu-sdk-libs:TARGET", |
225 | - "ubuntu-sdk-libs-dev:TARGET", |
226 | + "ubuntu-sdk-libs:{TARGET}", |
227 | + "ubuntu-sdk-libs-dev:{TARGET}", |
228 | + # the native build tools |
229 | + "ubuntu-sdk-libs-tools", |
230 | + # FIXME: see |
231 | + # http://pad.lv/~mvo/oxide/crossbuild-friendly/+merge/234093 |
232 | + # we help the apt resolver here until the |
233 | + # oxideqt-codecs/oxidec-codecs-extras is sorted |
234 | + "oxideqt-codecs-extra", |
235 | ], |
236 | "ubuntu-core-15.04-dev1": [ |
237 | - "ubuntu-core-libs:TARGET", |
238 | - "ubuntu-core-libs-dev:TARGET", |
239 | + "ubuntu-core-libs:{TARGET}", |
240 | + "ubuntu-core-libs-dev:{TARGET}", |
241 | ], |
242 | } |
243 | |
244 | @@ -185,6 +195,56 @@ |
245 | non_meta_re = re.compile(r'^[a-zA-Z0-9+,./:=@_-]+$') |
246 | |
247 | |
248 | +GEOIP_SERVER = "http://geoip.ubuntu.com/lookup" |
249 | + |
250 | + |
251 | +def get_geoip_country_code_prefix(): |
252 | + click_no_local_mirror = os.environ.get('CLICK_NO_LOCAL_MIRROR', 'auto') |
253 | + if click_no_local_mirror == '1': |
254 | + return "" |
255 | + try: |
256 | + with urllib.request.urlopen(GEOIP_SERVER) as f: |
257 | + xml_data = f.read() |
258 | + et = ElementTree.fromstring(xml_data) |
259 | + return et.find("CountryCode").text.lower()+"." |
260 | + except (ElementTree.ParseError, urllib.error.URLError): |
261 | + pass |
262 | + return "" |
263 | + |
264 | +def generate_sources(series, native_arch, target_arch, |
265 | + archive_mirror, ports_mirror, components): |
266 | + """Generate a list of strings for apts sources.list. |
267 | + Arguments: |
268 | + series -- the distro series (e.g. vivid) |
269 | + native_arch -- the native architecture (e.g. amd64) |
270 | + target_arch -- the target architecture (e.g. armhf) |
271 | + archive_mirror -- main mirror, e.g. http://archive.ubuntu.com/ubuntu |
272 | + ports_mirror -- ports mirror, e.g. http://ports.ubuntu.com/ubuntu-ports |
273 | + components -- the components as string, e.g. "main restricted universe" |
274 | + """ |
275 | + pockets = ['%s' % series] |
276 | + for pocket in ['updates', 'security']: |
277 | + pockets.append('%s-%s' % (series, pocket)) |
278 | + sources = [] |
279 | + # write binary lines |
280 | + arches = [target_arch] |
281 | + if native_arch != target_arch: |
282 | + arches.append(native_arch) |
283 | + for arch in arches: |
284 | + if arch not in primary_arches: |
285 | + mirror = ports_mirror |
286 | + else: |
287 | + mirror = archive_mirror |
288 | + for pocket in pockets: |
289 | + sources.append("deb [arch=%s] %s %s %s" % |
290 | + (arch, mirror, pocket, components)) |
291 | + # write source lines |
292 | + for pocket in pockets: |
293 | + sources.append("deb-src %s %s %s" % |
294 | + (archive_mirror, pocket, components)) |
295 | + return sources |
296 | + |
297 | + |
298 | def shell_escape(command): |
299 | escaped = [] |
300 | for arg in command: |
301 | @@ -247,11 +307,7 @@ |
302 | if chroots_dir is None: |
303 | chroots_dir = "/var/lib/schroot/chroots" |
304 | self.chroots_dir = chroots_dir |
305 | - # this doesn't work because we are running this under sudo |
306 | - if 'DEBOOTSTRAP_MIRROR' in os.environ: |
307 | - self.archive = os.environ['DEBOOTSTRAP_MIRROR'] |
308 | - else: |
309 | - self.archive = "http://archive.ubuntu.com/ubuntu" |
310 | + |
311 | if "SUDO_USER" in os.environ: |
312 | self.user = os.environ["SUDO_USER"] |
313 | elif "PKEXEC_UID" in os.environ: |
314 | @@ -330,31 +386,6 @@ |
315 | users="\n".join(users), |
316 | mount=mount)) |
317 | |
318 | - def _generate_sources(self, series, native_arch, target_arch, components): |
319 | - ports_mirror = "http://ports.ubuntu.com/ubuntu-ports" |
320 | - pockets = ['%s' % series] |
321 | - for pocket in ['updates', 'security']: |
322 | - pockets.append('%s-%s' % (series, pocket)) |
323 | - sources = [] |
324 | - # write binary lines |
325 | - arches = [target_arch] |
326 | - if native_arch != target_arch: |
327 | - arches.append(native_arch) |
328 | - for arch in arches: |
329 | - if arch not in primary_arches: |
330 | - mirror = ports_mirror |
331 | - else: |
332 | - mirror = self.archive |
333 | - for pocket in pockets: |
334 | - sources.append("deb [arch=%s] %s %s %s" % |
335 | - (arch, mirror, pocket, components)) |
336 | - # write source lines |
337 | - for pocket in pockets: |
338 | - sources.append("deb-src %s %s %s" % |
339 | - (self.archive, pocket, components)) |
340 | - |
341 | - return sources |
342 | - |
343 | def _generate_daemon_policy(self, mount): |
344 | daemon_policy = "%s/usr/sbin/policy-rc.d" % mount |
345 | with open(daemon_policy, "w") as policy: |
346 | @@ -409,7 +440,7 @@ |
347 | build_pkgs=' '.join(build_pkgs))) |
348 | return finish_script |
349 | |
350 | - def _debootstrap(self, components, mount): |
351 | + def _debootstrap(self, components, mount, archive): |
352 | subprocess.check_call([ |
353 | "debootstrap", |
354 | "--arch", self.native_arch, |
355 | @@ -417,7 +448,7 @@ |
356 | "--components=%s" % ','.join(components), |
357 | self.series, |
358 | mount, |
359 | - self.archive |
360 | + archive |
361 | ]) |
362 | |
363 | @property |
364 | @@ -467,22 +498,37 @@ |
365 | proxy = os.environ["http_proxy"] |
366 | if not proxy: |
367 | proxy = subprocess.check_output( |
368 | - 'unset x; eval "$(apt-config shell x Acquire::HTTP::Proxy)"; echo "$x"', |
369 | + 'unset x; eval "$(apt-config shell x Acquire::HTTP::Proxy)"; \ |
370 | + echo "$x"', |
371 | shell=True, universal_newlines=True).strip() |
372 | build_pkgs = [ |
373 | - "build-essential", "fakeroot", |
374 | - "apt-utils", self._make_cross_package("g++"), |
375 | - self._make_cross_package("pkg-config"), "cmake", |
376 | - "dpkg-cross", "libc-dev:%s" % self.target_arch |
377 | - ] |
378 | + # sort alphabetically |
379 | + "apt-utils", |
380 | + "build-essential", |
381 | + "cmake", |
382 | + "dpkg-cross", |
383 | + "fakeroot", |
384 | + "libc-dev:%s" % self.target_arch, |
385 | + # build pkg names dynamically |
386 | + self._make_cross_package("g++"), |
387 | + self._make_cross_package("pkg-config"), |
388 | + ] |
389 | for package in extra_packages.get(self.framework_base, []): |
390 | - package = package.replace(":TARGET", ":%s" % self.target_arch) |
391 | + package = package.format(TARGET=self.target_arch) |
392 | build_pkgs.append(package) |
393 | os.makedirs(mount) |
394 | - self._debootstrap(components, mount) |
395 | - sources = self._generate_sources(self.series, self.native_arch, |
396 | - self.target_arch, |
397 | - ' '.join(components)) |
398 | + |
399 | + country_code = get_geoip_country_code_prefix() |
400 | + archive = "http://%sarchive.ubuntu.com/ubuntu" % country_code |
401 | + ports_mirror = "http://%sports.ubuntu.com/ubuntu-ports" % country_code |
402 | + # this doesn't work because we are running this under sudo |
403 | + if 'DEBOOTSTRAP_MIRROR' in os.environ: |
404 | + archive = os.environ['DEBOOTSTRAP_MIRROR'] |
405 | + self._debootstrap(components, mount, archive) |
406 | + sources = generate_sources(self.series, self.native_arch, |
407 | + self.target_arch, |
408 | + archive, ports_mirror, |
409 | + ' '.join(components)) |
410 | with open("%s/etc/apt/sources.list" % mount, "w") as sources_list: |
411 | for line in sources: |
412 | print(line, file=sources_list) |
413 | |
414 | === modified file 'click/commands/install.py' |
415 | --- click/commands/install.py 2014-09-10 12:28:49 +0000 |
416 | +++ click/commands/install.py 2015-02-13 12:37:00 +0000 |
417 | @@ -46,6 +46,9 @@ |
418 | parser.add_option( |
419 | "--allow-unauthenticated", default=False, action="store_true", |
420 | help="allow installing packages with no signatures") |
421 | + parser.add_option( |
422 | + "--verbose", default=False, action="store_true", |
423 | + help="be more verbose on install") |
424 | options, args = parser.parse_args(argv) |
425 | if len(args) < 1: |
426 | parser.error("need package file name") |
427 | @@ -59,7 +62,8 @@ |
428 | allow_unauthenticated=options.allow_unauthenticated) |
429 | try: |
430 | installer.install( |
431 | - package_path, user=options.user, all_users=options.all_users) |
432 | + package_path, user=options.user, all_users=options.all_users, |
433 | + quiet=not options.verbose) |
434 | except ClickInstallerError as e: |
435 | print("Cannot install %s: %s" % (package_path, e), file=sys.stderr) |
436 | return 1 |
437 | |
438 | === modified file 'click/install.py' |
439 | --- click/install.py 2014-09-10 11:50:18 +0000 |
440 | +++ click/install.py 2015-02-13 12:37:00 +0000 |
441 | @@ -347,7 +347,7 @@ |
442 | os.mkdir(os.path.join(admin_dir, "updates")) |
443 | os.mkdir(os.path.join(admin_dir, "triggers")) |
444 | |
445 | - def _unpack(self, path, user=None, all_users=False): |
446 | + def _unpack(self, path, user=None, all_users=False, quiet=True): |
447 | package_name, package_version = self.audit(path, check_arch=True) |
448 | |
449 | # Is this package already unpacked in an underlay (non-topmost) |
450 | @@ -401,9 +401,20 @@ |
451 | kwargs = {} |
452 | if sys.version >= "3.2": |
453 | kwargs["pass_fds"] = (fd.fileno(),) |
454 | - subprocess.check_call( |
455 | - command, preexec_fn=partial(self._install_preexec, inst_dir), |
456 | - env=env, **kwargs) |
457 | + if quiet: |
458 | + fn = subprocess.check_output |
459 | + kwargs["stderr"] = subprocess.STDOUT |
460 | + else: |
461 | + fn = subprocess.check_call |
462 | + try: |
463 | + fn(command, |
464 | + preexec_fn=partial(self._install_preexec, inst_dir), |
465 | + env=env, universal_newlines=True, |
466 | + **kwargs) |
467 | + except subprocess.CalledProcessError as e: |
468 | + logging.error("%s failed with exit_code %s:\n%s" % ( |
469 | + command, e.returncode, e.output)) |
470 | + raise |
471 | for dirpath, dirnames, filenames in os.walk(inst_dir): |
472 | for entry in dirnames + filenames: |
473 | entry_path = os.path.join(dirpath, entry) |
474 | @@ -441,9 +452,9 @@ |
475 | |
476 | return package_name, package_version, old_version |
477 | |
478 | - def install(self, path, user=None, all_users=False): |
479 | + def install(self, path, user=None, all_users=False, quiet=True): |
480 | package_name, package_version, old_version = self._unpack( |
481 | - path, user=user, all_users=all_users) |
482 | + path, user=user, all_users=all_users, quiet=quiet) |
483 | |
484 | if user is not None or all_users: |
485 | if all_users: |
486 | |
487 | === modified file 'click/tests/test_build.py' |
488 | --- click/tests/test_build.py 2014-09-04 13:41:30 +0000 |
489 | +++ click/tests/test_build.py 2015-02-13 12:37:00 +0000 |
490 | @@ -134,6 +134,8 @@ |
491 | touch(os.path.join(scratch, ".git", "config")) |
492 | with mkfile(os.path.join(scratch, "toplevel")) as f: |
493 | f.write("test /toplevel\n") |
494 | + os.symlink( |
495 | + "file-does-not-exist", os.path.join(scratch, "broken-symlink")) |
496 | with mkfile(os.path.join(scratch, "manifest.json")) as f: |
497 | json.dump({ |
498 | "name": "com.example.test", |
499 | |
500 | === modified file 'click/tests/test_chroot.py' |
501 | --- click/tests/test_chroot.py 2014-07-02 09:07:13 +0000 |
502 | +++ click/tests/test_chroot.py 2015-02-13 12:37:00 +0000 |
503 | @@ -28,6 +28,7 @@ |
504 | |
505 | from click.chroot import ( |
506 | ClickChroot, |
507 | + generate_sources, |
508 | strip_dev_series_from_framework, |
509 | ) |
510 | from click.tests.helpers import TestCase, mock |
511 | @@ -48,7 +49,7 @@ |
512 | self._maint_kwargs = kwargs |
513 | return 0 |
514 | |
515 | - def _debootstrap(self, components, mount): |
516 | + def _debootstrap(self, components, mount, archive_server): |
517 | os.makedirs(os.path.join(mount, "etc", "apt")) |
518 | os.makedirs(os.path.join(mount, "usr", "sbin")) |
519 | os.makedirs(os.path.join(mount, "sbin")) |
520 | @@ -110,8 +111,10 @@ |
521 | def test_gen_sources_archive_only(self): |
522 | chroot = ClickChroot("amd64", "ubuntu-sdk-13.10", series="trusty") |
523 | chroot.native_arch = "i386" |
524 | - sources = chroot._generate_sources( |
525 | + sources = generate_sources( |
526 | chroot.series, chroot.native_arch, chroot.target_arch, |
527 | + "http://archive.ubuntu.com/ubuntu", |
528 | + "http://ports.ubuntu.com/ubuntu-ports", |
529 | "main") |
530 | self.assertEqual([ |
531 | 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty main', |
532 | @@ -128,8 +131,10 @@ |
533 | def test_gen_sources_mixed_archive_ports(self): |
534 | chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty") |
535 | chroot.native_arch = "i386" |
536 | - sources = chroot._generate_sources( |
537 | + sources = generate_sources( |
538 | chroot.series, chroot.native_arch, chroot.target_arch, |
539 | + "http://archive.ubuntu.com/ubuntu", |
540 | + "http://ports.ubuntu.com/ubuntu-ports", |
541 | "main") |
542 | self.assertEqual([ |
543 | 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main', |
544 | @@ -146,8 +151,10 @@ |
545 | def test_gen_sources_ports_only(self): |
546 | chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty") |
547 | chroot.native_arch = "armel" |
548 | - sources = chroot._generate_sources( |
549 | + sources = generate_sources( |
550 | chroot.series, chroot.native_arch, chroot.target_arch, |
551 | + "http://archive.ubuntu.com/ubuntu", |
552 | + "http://ports.ubuntu.com/ubuntu-ports", |
553 | "main") |
554 | self.assertEqual([ |
555 | 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main', |
556 | @@ -164,8 +171,10 @@ |
557 | def test_gen_sources_native(self): |
558 | chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty") |
559 | chroot.native_arch = "i386" |
560 | - sources = chroot._generate_sources( |
561 | + sources = generate_sources( |
562 | chroot.series, chroot.native_arch, chroot.target_arch, |
563 | + "http://archive.ubuntu.com/ubuntu", |
564 | + "http://ports.ubuntu.com/ubuntu-ports", |
565 | "main") |
566 | self.assertEqual([ |
567 | 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main', |
568 | |
569 | === modified file 'click/tests/test_install.py' |
570 | --- click/tests/test_install.py 2014-08-19 06:32:16 +0000 |
571 | +++ click/tests/test_install.py 2015-02-13 12:37:00 +0000 |
572 | @@ -456,18 +456,12 @@ |
573 | with self.run_in_subprocess( |
574 | "click_get_frameworks_dir") as (enter, preloads): |
575 | enter() |
576 | - original_call = subprocess.call |
577 | + original_call = subprocess.check_output |
578 | |
579 | def call_side_effect(*args, **kwargs): |
580 | - if "TEST_VERBOSE" in os.environ: |
581 | - return original_call( |
582 | - ["touch", os.path.join(self.temp_dir, "sentinel")], |
583 | - **kwargs) |
584 | - else: |
585 | - with open("/dev/null", "w") as devnull: |
586 | - return original_call( |
587 | - ["touch", os.path.join(self.temp_dir, "sentinel")], |
588 | - stdout=devnull, stderr=devnull, **kwargs) |
589 | + return original_call( |
590 | + ["touch", os.path.join(self.temp_dir, "sentinel")], |
591 | + **kwargs) |
592 | |
593 | path = self.make_fake_package( |
594 | control_fields={ |
595 | @@ -490,7 +484,7 @@ |
596 | db.add(root) |
597 | installer = ClickInstaller(db) |
598 | self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) |
599 | - with mock.patch("subprocess.call") as mock_call: |
600 | + with mock.patch("subprocess.check_output") as mock_call: |
601 | mock_call.side_effect = call_side_effect |
602 | self.assertRaises( |
603 | subprocess.CalledProcessError, installer.install, path) |
604 | |
605 | === modified file 'debian/changelog' |
606 | --- debian/changelog 2014-11-14 12:29:14 +0000 |
607 | +++ debian/changelog 2015-02-13 12:37:00 +0000 |
608 | @@ -1,3 +1,33 @@ |
609 | +click (0.4.37) UNRELEASED; urgency=low |
610 | + |
611 | + [ Michael Vogt ] |
612 | + * lp:~mvo/click/no-error-no-missing-systemctl: |
613 | + - fix a spurious error message on systems without systemctl |
614 | + * lp:~mvo/click/do-not-crash-in-build-on-broken-symlinks: |
615 | + - do not crash when building a click package that contains broken |
616 | + symlinks |
617 | + * lp:~mvo/click/dpkg-less-verbose: |
618 | + - do not show dpkg output on install unless --verbose is used |
619 | + * lp:~mvo/click/lp1394256-run-user-hooks: |
620 | + - ensures that click user hooks are run for all logged in users when |
621 | + click is used with "--all-users". |
622 | + * lp:~mvo/click/qt5-qmake-cross-armhf: |
623 | + - add qt5-qmake-arm-linux-gnueabihf to chroot (LP: #1393698) |
624 | + * lp:~mvo/click/chroot-15.04-multiarch: |
625 | + - add ubuntu-sdk-libs-tools and oxide-codecs-extra to the chroot |
626 | + * lp:~mvo/click/lp1394256-run-user-hooks-on-remove-too: |
627 | + - Run the click remove user hooks for all logged in users. |
628 | + * click/chroot.py: |
629 | + - use string.format() for chroot TARGET selection |
630 | + * skip 0.4.36 version and go straight to 0.4.37 (LP: #1418086) |
631 | + |
632 | + [ Zoltan Balogh ] |
633 | + * lp:~bzoltan/click/vivid-transition_mirrors: |
634 | + - use geoip to guess the most suitable country mirror when creating |
635 | + the click chroot |
636 | + |
637 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 14 Nov 2014 12:29:14 +0000 |
638 | + |
639 | click (0.4.35) vivid; urgency=low |
640 | |
641 | [ Michael Vogt ] |
642 | |
643 | === modified file 'debian/click.postinst' |
644 | --- debian/click.postinst 2014-10-14 09:47:48 +0000 |
645 | +++ debian/click.postinst 2015-02-13 12:37:00 +0000 |
646 | @@ -13,7 +13,9 @@ |
647 | |
648 | # dh-systemd has no support yet for user systemd units |
649 | # so we need to do this manually here |
650 | - systemctl --global enable click-user-hooks.service || true |
651 | + if which systemctl >/dev/null 2>&1; then |
652 | + systemctl --global enable click-user-hooks.service || true |
653 | + fi |
654 | fi |
655 | |
656 | #DEBHELPER# |
657 | |
658 | === modified file 'lib/click/user.vala' |
659 | --- lib/click/user.vala 2014-09-29 13:23:12 +0000 |
660 | +++ lib/click/user.vala 2015-02-13 12:37:00 +0000 |
661 | @@ -33,8 +33,21 @@ |
662 | * symlinks per user. |
663 | */ |
664 | |
665 | + |
666 | namespace Click { |
667 | |
668 | + struct LogindUser { |
669 | + uint32 uid; |
670 | + string name; |
671 | + string ObjectPath; |
672 | + } |
673 | + |
674 | + /* the logind dbus interface */ |
675 | + [DBus (name = "org.freedesktop.login1.Manager")] |
676 | + interface LogindManager : Object { |
677 | + public abstract LogindUser[] ListUsers () throws IOError; |
678 | + } |
679 | + |
680 | /* Pseudo-usernames selected to be invalid as a real username, and alluding |
681 | * to group syntaxes used in other systems. |
682 | */ |
683 | @@ -596,6 +609,53 @@ |
684 | if (! is_pseudo_user) |
685 | package_install_hooks (db, package, |
686 | old_version, version, name); |
687 | + |
688 | + // run user hooks for all logged in users |
689 | + if (name == ALL_USERS) |
690 | + run_user_install_hooks_for_all_logged_in_users (package, old_version, version); |
691 | + } |
692 | + |
693 | + private string[] |
694 | + get_logged_in_users() |
695 | + { |
696 | + string[] logged_in_users = {}; |
697 | + try { |
698 | + LogindManager logind = Bus.get_proxy_sync ( |
699 | + BusType.SYSTEM, |
700 | + "org.freedesktop.login1", |
701 | + "/org/freedesktop/login1"); |
702 | + var users = logind.ListUsers(); |
703 | + foreach (LogindUser user in users) |
704 | + { |
705 | + // FIXME: ideally we would read from /etc/adduser.conf |
706 | + if(user.uid >= 1000 && user.uid <= 30000) |
707 | + { |
708 | + logged_in_users += user.name; |
709 | + } |
710 | + } |
711 | + } catch (Error e) { |
712 | + warning ("Can not connect to logind"); |
713 | + } |
714 | + return logged_in_users; |
715 | + } |
716 | + |
717 | + private void |
718 | + run_user_install_hooks_for_all_logged_in_users (string package, |
719 | + string? old_version, |
720 | + string version) throws IOError |
721 | + { |
722 | + foreach (string username in get_logged_in_users()) |
723 | + package_install_hooks (db, package, |
724 | + old_version, version, username); |
725 | + } |
726 | + |
727 | + private void |
728 | + run_user_remove_hooks_for_all_logged_in_users (string package, |
729 | + string old_version) throws IOError |
730 | + { |
731 | + foreach (string username in get_logged_in_users()) |
732 | + package_remove_hooks (db, package, |
733 | + old_version, username); |
734 | } |
735 | |
736 | private bool |
737 | @@ -688,6 +748,10 @@ |
738 | |
739 | if (! is_pseudo_user) |
740 | package_remove_hooks (db, package, old_version, name); |
741 | + |
742 | + // run user hooks for all logged in users |
743 | + if (name == ALL_USERS) |
744 | + run_user_remove_hooks_for_all_logged_in_users (package, old_version); |
745 | } |
746 | |
747 | /** |