Merge lp:click/devel into lp:click

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 444
Merged at revision: 426
Proposed branch: lp:click/devel
Merge into: lp:click
Diff against target: 1791 lines (+740/-253)
14 files modified
click/chroot.py (+82/-25)
click/commands/__init__.py (+1/-0)
click/commands/chroot.py (+69/-9)
click/commands/framework.py (+42/-0)
click/tests/helpers.py (+22/-0)
click/tests/test_build.py (+10/-1)
click/tests/test_chroot.py (+123/-0)
click/tests/test_hooks.py (+261/-196)
click/tests/test_install.py (+9/-9)
debian/changelog (+38/-0)
debian/control (+1/-1)
doc/index.rst (+4/-2)
doc/manpage.rst (+6/-0)
lib/click/hooks.vala (+72/-10)
To merge this branch: bzr merge lp:click/devel
Reviewer Review Type Date Requested Status
Colin Watson Approve
Review via email: mp+220239@code.launchpad.net

Commit message

Click 0.4.23: handle framework removal, x86 chroot support, and various other small fixes.

Description of the change

  [ Michael Vogt ]
  * Show human-readable error message when a click chroot subcommand fails
    because of existing or non-existing chroots (LP: #1296820).
  * Selectively disable logging on some tests to avoid message spam during
    the test runs.
  * When running hooks, remove hook symlinks if framework requirements are
    not met (LP: #1271944).
  * Cleanup the chroot if "click chroot create" fails (unless
    --keep-broken-chroot is used)
  * Fix sources.list generation when native_arch and target_arch are on the
    same archive server (part of LP #1319153).
  * Add "click framework list" command to list available frameworks
    (LP: #1294659).

  [ Pete Woods ]
  * Add libunity-scopes-dev package to chroot (LP: #1320786).

  [ Sergio Schvezov ]
  * click chroot creation depends on dpkg-architecture, so recommend
    dpkg-dev.

  [ Colin Watson ]
  * chroot: Handle the case where we can execute binaries for the target
    architecture directly and thus don't need a cross-compiler
    (LP: #1319153).

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
lp:click/devel updated
445. By Colin Watson

releasing package click version 0.4.23

446. By Colin Watson

chroot: Force dpkg-architecture to recalculate everything rather than
picking up values from the environment, to avoid the test suite getting
confused by environment variables exported by dpkg-buildpackage.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'click/chroot.py'
2--- click/chroot.py 2014-03-31 11:11:26 +0000
3+++ click/chroot.py 2014-05-20 13:13:48 +0000
4@@ -22,6 +22,8 @@
5 __all__ = [
6 "ClickChroot",
7 "ClickChrootException",
8+ "ClickChrootAlreadyExistsException",
9+ "ClickChrootDoesNotExistException",
10 ]
11
12
13@@ -72,6 +74,7 @@
14 "libqt5svg5-dev:TARGET",
15 "libqt5webkit5-dev:TARGET",
16 "libqt5xmlpatterns5-dev:TARGET",
17+ "libunity-scopes-dev:TARGET",
18 "qt3d5-dev:TARGET",
19 "qt5-default:TARGET",
20 "qtbase5-dev:TARGET",
21@@ -104,6 +107,17 @@
22
23
24 class ClickChrootException(Exception):
25+ """A generic issue with the chroot"""
26+ pass
27+
28+
29+class ClickChrootAlreadyExistsException(ClickChrootException):
30+ """The chroot already exists"""
31+ pass
32+
33+
34+class ClickChrootDoesNotExistException(ClickChrootException):
35+ """A chroot with that name does not exist yet"""
36 pass
37
38
39@@ -118,9 +132,10 @@
40 series = framework_series[self.framework_base]
41 self.series = series
42 self.session = session
43- self.native_arch = subprocess.check_output(
44+ system_arch = subprocess.check_output(
45 ["dpkg", "--print-architecture"],
46 universal_newlines=True).strip()
47+ self.native_arch = self._get_native_arch(system_arch, self.target_arch)
48 self.chroots_dir = "/var/lib/schroot/chroots"
49 # this doesn't work because we are running this under sudo
50 if 'DEBOOTSTRAP_MIRROR' in os.environ:
51@@ -135,11 +150,35 @@
52 self.user = pwd.getpwuid(os.getuid()).pw_name
53 self.dpkg_architecture = self._dpkg_architecture()
54
55+ def _get_native_arch(self, system_arch, target_arch):
56+ """Determine the proper native architecture for a chroot.
57+
58+ Some combinations of system and target architecture do not require
59+ cross-building, so in these cases we just create a chroot suitable
60+ for native building.
61+ """
62+ if (system_arch, target_arch) in (
63+ ("amd64", "i386"),
64+ # This will only work if the system is running a 64-bit
65+ # kernel; but there's no alternative since no i386-to-amd64
66+ # cross-compiler is available in the Ubuntu archive.
67+ ("i386", "amd64"),
68+ ):
69+ return target_arch
70+ else:
71+ return system_arch
72+
73 def _dpkg_architecture(self):
74 dpkg_architecture = {}
75 command = ["dpkg-architecture", "-a%s" % self.target_arch]
76 env = dict(os.environ)
77 env["CC"] = "true"
78+ # Force dpkg-architecture to recalculate everything rather than
79+ # picking up values from the environment, which will be present when
80+ # running the test suite under dpkg-buildpackage.
81+ for key in list(env):
82+ if key.startswith("DEB_BUILD_") or key.startswith("DEB_HOST_"):
83+ del env[key]
84 lines = subprocess.check_output(
85 command, env=env, universal_newlines=True).splitlines()
86 for line in lines:
87@@ -156,18 +195,23 @@
88 for pocket in ['updates', 'security']:
89 pockets.append('%s-%s' % (series, pocket))
90 sources = []
91- if target_arch not in primary_arches:
92- for pocket in pockets:
93- sources.append("deb [arch=%s] %s %s %s" %
94- (target_arch, ports_mirror, pocket, components))
95- sources.append("deb-src %s %s %s" %
96- (ports_mirror, pocket, components))
97- if native_arch in primary_arches:
98- for pocket in pockets:
99- sources.append("deb [arch=%s] %s %s %s" %
100- (native_arch, self.archive, pocket, components))
101- sources.append("deb-src %s %s %s" %
102- (self.archive, pocket, components))
103+ # write binary lines
104+ arches = [target_arch]
105+ if native_arch != target_arch:
106+ arches.append(native_arch)
107+ for arch in arches:
108+ if arch not in primary_arches:
109+ mirror = ports_mirror
110+ else:
111+ mirror = self.archive
112+ for pocket in pockets:
113+ sources.append("deb [arch=%s] %s %s %s" %
114+ (arch, mirror, pocket, components))
115+ # write source lines
116+ for pocket in pockets:
117+ sources.append("deb-src %s %s %s" %
118+ (self.archive, pocket, components))
119+
120 return sources
121
122 @property
123@@ -195,9 +239,16 @@
124 mode = stat.S_IMODE(os.stat(path).st_mode)
125 os.chmod(path, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
126
127- def create(self):
128+ def _make_cross_package(self, prefix):
129+ if self.native_arch == self.target_arch:
130+ return prefix
131+ else:
132+ target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"]
133+ return "%s-%s" % (prefix, target_tuple)
134+
135+ def create(self, keep_broken_chroot_on_fail=False):
136 if self.exists():
137- raise ClickChrootException(
138+ raise ClickChrootAlreadyExistsException(
139 "Chroot %s already exists" % self.full_name)
140 components = ["main", "restricted", "universe", "multiverse"]
141 mount = "%s/%s" % (self.chroots_dir, self.full_name)
142@@ -208,11 +259,10 @@
143 proxy = subprocess.check_output(
144 'unset x; eval "$(apt-config shell x Acquire::HTTP::Proxy)"; echo "$x"',
145 shell=True, universal_newlines=True).strip()
146- target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"]
147 build_pkgs = [
148 "build-essential", "fakeroot",
149- "apt-utils", "g++-%s" % target_tuple,
150- "pkg-config-%s" % target_tuple, "cmake",
151+ "apt-utils", self._make_cross_package("g++"),
152+ self._make_cross_package("pkg-config"), "cmake",
153 "dpkg-cross", "libc-dev:%s" % self.target_arch
154 ]
155 for package in extra_packages.get(self.framework_base, []):
156@@ -312,11 +362,18 @@
157 print("apt-get clean", file=finish)
158 self._make_executable(finish_script)
159 command = ["/finish.sh"]
160- return self.maint(*command)
161+ ret_code = self.maint(*command)
162+ if ret_code != 0 and not keep_broken_chroot_on_fail:
163+ # cleanup on failure
164+ self.destroy()
165+ raise ClickChrootException(
166+ "Failed to create chroot '{}' (exit status {})".format(
167+ self.full_name, ret_code))
168+ return ret_code
169
170 def run(self, *args):
171 if not self.exists():
172- raise ClickChrootException(
173+ raise ClickChrootDoesNotExistException(
174 "Chroot %s does not exist" % self.full_name)
175 command = ["schroot", "-c"]
176 if self.session:
177@@ -353,7 +410,7 @@
178
179 def install(self, *pkgs):
180 if not self.exists():
181- raise ClickChrootException(
182+ raise ClickChrootDoesNotExistException(
183 "Chroot %s does not exist" % self.full_name)
184 ret = self.update()
185 if ret != 0:
186@@ -375,7 +432,7 @@
187
188 def upgrade(self):
189 if not self.exists():
190- raise ClickChrootException(
191+ raise ClickChrootDoesNotExistException(
192 "Chroot %s does not exist" % self.full_name)
193 ret = self.update()
194 if ret != 0:
195@@ -388,7 +445,7 @@
196
197 def destroy(self):
198 if not self.exists():
199- raise ClickChrootException(
200+ raise ClickChrootDoesNotExistException(
201 "Chroot %s does not exist" % self.full_name)
202 chroot_config = "/etc/schroot/chroot.d/%s" % self.full_name
203 os.remove(chroot_config)
204@@ -398,7 +455,7 @@
205
206 def begin_session(self):
207 if not self.exists():
208- raise ClickChrootException(
209+ raise ClickChrootDoesNotExistException(
210 "Chroot %s does not exist" % self.full_name)
211 command = ["schroot", "-c", self.full_name, "--begin-session",
212 "--session-name", self.full_session_name]
213@@ -407,7 +464,7 @@
214
215 def end_session(self):
216 if not self.exists():
217- raise ClickChrootException(
218+ raise ClickChrootDoesNotExistException(
219 "Chroot %s does not exist" % self.full_name)
220 command = ["schroot", "-c", self.full_session_name, "--end-session"]
221 subprocess.check_call(command)
222
223=== modified file 'click/commands/__init__.py'
224--- click/commands/__init__.py 2013-10-31 20:17:19 +0000
225+++ click/commands/__init__.py 2014-05-20 13:13:48 +0000
226@@ -24,6 +24,7 @@
227 "chroot",
228 "contents",
229 "desktophook",
230+ "framework",
231 "hook",
232 "info",
233 "install",
234
235=== modified file 'click/commands/chroot.py'
236--- click/commands/chroot.py 2014-03-31 11:11:26 +0000
237+++ click/commands/chroot.py 2014-05-20 13:13:48 +0000
238@@ -20,9 +20,14 @@
239 from __future__ import print_function
240
241 from argparse import ArgumentParser, REMAINDER
242+from contextlib import contextmanager
243 import os
244
245-from click.chroot import ClickChroot
246+from click.chroot import (
247+ ClickChroot,
248+ ClickChrootAlreadyExistsException,
249+ ClickChrootDoesNotExistException,
250+)
251 from click import osextras
252
253
254@@ -31,6 +36,25 @@
255 parser.error("must be run as root; try sudo")
256
257
258+@contextmanager
259+def message_on_error(exc, msg):
260+ """
261+ Context Manager that prints the error message 'msg' on exception 'exc'
262+ """
263+ try:
264+ yield
265+ except exc:
266+ print(msg)
267+
268+
269+# FIXME: i18n(?)
270+class ErrorMessages:
271+ EXISTS = """A chroot for that name and architecture already exists.
272+Please see the man-page how to use it."""
273+ NOT_EXISTS = """A chroot for that name and architecture does not exist.
274+Please use 'create' to create it."""
275+
276+
277 def create(parser, args):
278 if not osextras.find_on_path("debootstrap"):
279 parser.error(
280@@ -38,20 +62,32 @@
281 "debootstrap")
282 requires_root(parser)
283 chroot = ClickChroot(args.architecture, args.framework, series=args.series)
284- return chroot.create()
285+ with message_on_error(
286+ ClickChrootAlreadyExistsException, ErrorMessages.EXISTS):
287+ return chroot.create(args.keep_broken_chroot)
288+ # if we reach this point there was a error so return exit_status 1
289+ return 1
290
291
292 def install(parser, args):
293 packages = args.packages
294 chroot = ClickChroot(args.architecture, args.framework)
295- return chroot.install(*packages)
296+ with message_on_error(
297+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
298+ return chroot.install(*packages)
299+ # if we reach this point there was a error so return exit_status 1
300+ return 1
301
302
303 def destroy(parser, args):
304 requires_root(parser)
305 # ask for confirmation?
306 chroot = ClickChroot(args.architecture, args.framework)
307- return chroot.destroy()
308+ with message_on_error(
309+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
310+ return chroot.destroy()
311+ # if we reach this point there was a error so return exit_status 1
312+ return 1
313
314
315 def execute(parser, args):
316@@ -60,7 +96,11 @@
317 program = ["/bin/bash"]
318 chroot = ClickChroot(
319 args.architecture, args.framework, session=args.session)
320- return chroot.run(*program)
321+ with message_on_error(
322+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
323+ return chroot.run(*program)
324+ # if we reach this point there was a error so return exit_status 1
325+ return 1
326
327
328 def maint(parser, args):
329@@ -69,24 +109,40 @@
330 program = ["/bin/bash"]
331 chroot = ClickChroot(
332 args.architecture, args.framework, session=args.session)
333- return chroot.maint(*program)
334+ with message_on_error(
335+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
336+ return chroot.maint(*program)
337+ # if we reach this point there was a error so return exit_status 1
338+ return 1
339
340
341 def upgrade(parser, args):
342 chroot = ClickChroot(args.architecture, args.framework)
343- return chroot.upgrade()
344+ with message_on_error(
345+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
346+ return chroot.upgrade()
347+ # if we reach this point there was a error so return exit_status 1
348+ return 1
349
350
351 def begin_session(parser, args):
352 chroot = ClickChroot(
353 args.architecture, args.framework, session=args.session)
354- return chroot.begin_session()
355+ with message_on_error(
356+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
357+ return chroot.begin_session()
358+ # if we reach this point there was a error so return exit_status 1
359+ return 1
360
361
362 def end_session(parser, args):
363 chroot = ClickChroot(
364 args.architecture, args.framework, session=args.session)
365- return chroot.end_session()
366+ with message_on_error(
367+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
368+ return chroot.end_session()
369+ # if we reach this point there was a error so return exit_status 1
370+ return 1
371
372
373 def run(argv):
374@@ -107,6 +163,10 @@
375 create_parser = subparsers.add_parser(
376 "create",
377 help="create a chroot of the provided architecture")
378+ create_parser.add_argument(
379+ "-k", "--keep-broken-chroot", default=False, action="store_true",
380+ help="Keep the chroot even if creating it fails (default is to delete "
381+ "it)")
382 create_parser.set_defaults(func=create)
383 destroy_parser = subparsers.add_parser(
384 "destroy",
385
386=== added file 'click/commands/framework.py'
387--- click/commands/framework.py 1970-01-01 00:00:00 +0000
388+++ click/commands/framework.py 2014-05-20 13:13:48 +0000
389@@ -0,0 +1,42 @@
390+# Copyright (C) 2014 Canonical Ltd.
391+# Author: Michael Vogt <mvo@ubuntu.com>
392+
393+# This program is free software: you can redistribute it and/or modify
394+# it under the terms of the GNU General Public License as published by
395+# the Free Software Foundation; version 3 of the License.
396+#
397+# This program is distributed in the hope that it will be useful,
398+# but WITHOUT ANY WARRANTY; without even the implied warranty of
399+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
400+# GNU General Public License for more details.
401+#
402+# You should have received a copy of the GNU General Public License
403+# along with this program. If not, see <http://www.gnu.org/licenses/>.
404+
405+"""List available frameworks."""
406+
407+from __future__ import print_function
408+
409+from argparse import ArgumentParser
410+
411+from gi.repository import Click
412+
413+
414+def list(parser, args):
415+ for framework in Click.Framework.get_frameworks():
416+ print("%s" % framework.props.name)
417+ return 0
418+
419+
420+def run(argv):
421+ parser = ArgumentParser("click framework")
422+ subparsers = parser.add_subparsers()
423+ list_parser = subparsers.add_parser(
424+ "list",
425+ help="list available frameworks")
426+ list_parser.set_defaults(func=list)
427+ args = parser.parse_args(argv)
428+ if not hasattr(args, "func"):
429+ parser.print_help()
430+ return 1
431+ return args.func(parser, args)
432
433=== modified file 'click/tests/helpers.py'
434--- click/tests/helpers.py 2014-05-08 18:18:39 +0000
435+++ click/tests/helpers.py 2014-05-20 13:13:48 +0000
436@@ -29,6 +29,7 @@
437
438
439 import contextlib
440+from functools import wraps
441 import os
442 import re
443 import shutil
444@@ -45,6 +46,19 @@
445 from click.tests import gimock
446
447
448+def disable_logging(func):
449+ """Decorator to disable logging e.g. during a test"""
450+ @wraps(func)
451+ def wrapper(*args, **kwargs):
452+ import logging
453+ logging.disable(logging.CRITICAL)
454+ try:
455+ return func(*args, **kwargs)
456+ finally:
457+ logging.disable(logging.NOTSET)
458+ return wrapper
459+
460+
461 class TestCase(gimock.GIMockTestCase):
462 def setUp(self):
463 super(TestCase, self).setUp()
464@@ -108,6 +122,14 @@
465 self.assertRaisesGError(
466 "click_user_error-quark", code, callableObj, *args, **kwargs)
467
468+ def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]):
469+ frameworks_dir = self._create_mock_framework_dir(frameworks_dir)
470+ shutil.rmtree(frameworks_dir, ignore_errors=True)
471+ for framework in frameworks:
472+ self._create_mock_framework_file(framework)
473+ preloads["click_get_frameworks_dir"].side_effect = (
474+ lambda: self.make_string(frameworks_dir))
475+
476 def _create_mock_framework_dir(self, frameworks_dir=None):
477 if frameworks_dir is None:
478 frameworks_dir = os.path.join(self.temp_dir, "frameworks")
479
480=== modified file 'click/tests/test_build.py'
481--- click/tests/test_build.py 2014-05-05 13:10:19 +0000
482+++ click/tests/test_build.py 2014-05-20 13:13:48 +0000
483@@ -33,7 +33,12 @@
484
485 from click.build import ClickBuildError, ClickBuilder, ClickSourceBuilder
486 from click.preinst import static_preinst
487-from click.tests.helpers import TestCase, mkfile, touch
488+from click.tests.helpers import (
489+ disable_logging,
490+ mkfile,
491+ TestCase,
492+ touch,
493+)
494
495
496 # BAW 2013-04-15: Some tests require umask 022. Use this decorator to
497@@ -118,6 +123,7 @@
498 ["dpkg-deb", "-f", path, name],
499 universal_newlines=True).rstrip("\n")
500
501+ @disable_logging
502 @umask(0o22)
503 def test_build(self):
504 self.use_temp_dir()
505@@ -199,6 +205,7 @@
506 self.assertEqual(
507 "foo", os.readlink(os.path.join(extract_path, "bin", "bar")))
508
509+ @disable_logging
510 def test_build_excludes_dot_click(self):
511 self.use_temp_dir()
512 scratch = os.path.join(self.temp_dir, "scratch")
513@@ -218,6 +225,7 @@
514 subprocess.check_call(["dpkg-deb", "-x", path, extract_path])
515 self.assertEqual([], os.listdir(extract_path))
516
517+ @disable_logging
518 def test_build_multiple_architectures(self):
519 self.use_temp_dir()
520 scratch = os.path.join(self.temp_dir, "scratch")
521@@ -246,6 +254,7 @@
522 self.assertEqual(source_json, target_json)
523
524 # FIXME: DRY violation with test_build_multiple_architectures etc
525+ @disable_logging
526 def test_build_multiple_frameworks(self):
527 self.use_temp_dir()
528 scratch = os.path.join(self.temp_dir, "scratch")
529
530=== added file 'click/tests/test_chroot.py'
531--- click/tests/test_chroot.py 1970-01-01 00:00:00 +0000
532+++ click/tests/test_chroot.py 2014-05-20 13:13:48 +0000
533@@ -0,0 +1,123 @@
534+# Copyright (C) 2014 Canonical Ltd.
535+# Author: Michael Vogt
536+
537+# This program is free software: you can redistribute it and/or modify
538+# it under the terms of the GNU General Public License as published by
539+# the Free Software Foundation; version 3 of the License.
540+#
541+# This program is distributed in the hope that it will be useful,
542+# but WITHOUT ANY WARRANTY; without even the implied warranty of
543+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
544+# GNU General Public License for more details.
545+#
546+# You should have received a copy of the GNU General Public License
547+# along with this program. If not, see <http://www.gnu.org/licenses/>.
548+
549+"""Unit tests for click.chroot."""
550+
551+from __future__ import print_function
552+
553+__metaclass__ = type
554+__all__ = [
555+ 'TestClickChroot',
556+ ]
557+
558+
559+from click.tests.helpers import TestCase
560+from click.chroot import (
561+ ClickChroot,
562+)
563+
564+
565+class TestClickChroot(TestCase):
566+ def test_get_native_arch_amd64_to_amd64(self):
567+ chroot = ClickChroot("amd64", "ubuntu-sdk-14.04", series="trusty")
568+ self.assertEqual("amd64", chroot._get_native_arch("amd64", "amd64"))
569+
570+ def test_get_native_arch_amd64_to_armhf(self):
571+ chroot = ClickChroot("armhf", "ubuntu-sdk-14.04", series="trusty")
572+ self.assertEqual("amd64", chroot._get_native_arch("amd64", "armhf"))
573+
574+ def test_get_native_arch_amd64_to_i386(self):
575+ chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty")
576+ self.assertEqual("i386", chroot._get_native_arch("amd64", "i386"))
577+
578+ def test_gen_sources_archive_only(self):
579+ chroot = ClickChroot("amd64", "ubuntu-sdk-13.10", series="trusty")
580+ chroot.native_arch = "i386"
581+ sources = chroot._generate_sources(
582+ chroot.series, chroot.native_arch, chroot.target_arch,
583+ "main")
584+ self.assertEqual([
585+ 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty main',
586+ 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty-updates main',
587+ 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty-security main',
588+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main',
589+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main',
590+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main',
591+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
592+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
593+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
594+ ], sources)
595+
596+ def test_gen_sources_mixed_archive_ports(self):
597+ chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty")
598+ chroot.native_arch = "i386"
599+ sources = chroot._generate_sources(
600+ chroot.series, chroot.native_arch, chroot.target_arch,
601+ "main")
602+ self.assertEqual([
603+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main',
604+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-updates main',
605+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-security main',
606+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main',
607+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main',
608+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main',
609+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
610+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
611+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
612+ ], sources)
613+
614+ def test_gen_sources_ports_only(self):
615+ chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty")
616+ chroot.native_arch = "armel"
617+ sources = chroot._generate_sources(
618+ chroot.series, chroot.native_arch, chroot.target_arch,
619+ "main")
620+ self.assertEqual([
621+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main',
622+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-updates main',
623+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-security main',
624+ 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty main',
625+ 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty-updates main',
626+ 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty-security main',
627+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
628+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
629+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
630+ ], sources)
631+
632+ def test_gen_sources_native(self):
633+ chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty")
634+ chroot.native_arch = "i386"
635+ sources = chroot._generate_sources(
636+ chroot.series, chroot.native_arch, chroot.target_arch,
637+ "main")
638+ self.assertEqual([
639+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main',
640+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main',
641+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main',
642+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
643+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
644+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
645+ ], sources)
646+
647+ def test_make_cross_package_native(self):
648+ chroot = ClickChroot("amd64", "ubuntu-sdk-14.04", series="trusty")
649+ chroot.native_arch = "amd64"
650+ self.assertEqual("g++", chroot._make_cross_package("g++"))
651+
652+ def test_make_cross_package_cross(self):
653+ chroot = ClickChroot("armhf", "ubuntu-sdk-14.04", series="trusty")
654+ chroot.native_arch = "amd64"
655+ self.assertEqual(
656+ "g++-arm-linux-gnueabihf", chroot._make_cross_package("g++"))
657
658=== modified file 'click/tests/test_hooks.py'
659--- click/tests/test_hooks.py 2014-04-03 08:52:02 +0000
660+++ click/tests/test_hooks.py 2014-05-20 13:13:48 +0000
661@@ -95,6 +95,9 @@
662
663
664 class TestClickHookBase(TestCase):
665+
666+ TEST_USER = "test-user"
667+
668 def setUp(self):
669 super(TestClickHookBase, self).setUp()
670 self.use_temp_dir()
671@@ -102,11 +105,33 @@
672 self.db.add(self.temp_dir)
673 self.spawn_calls = []
674
675+ def _make_installed_click(self, package="test-1",version="1.0",
676+ json_data={},
677+ make_current=True,
678+ all_users=False):
679+ with mkfile_utf8(os.path.join(
680+ self.temp_dir, package, version, ".click", "info",
681+ "%s.manifest" % package)) as f:
682+ json.dump(json_data, f, ensure_ascii=False)
683+ if make_current:
684+ os.symlink(version, os.path.join(self.temp_dir, package, "current"))
685+ if all_users:
686+ db = Click.User.for_all_users(self.db)
687+ else:
688+ db = Click.User.for_user(self.db, self.TEST_USER)
689+ db.set_version(package, version)
690+
691+ def _make_hook_file(self, content, hookname="test"):
692+ hook_file = os.path.join(self.hooks_dir, "%s.hook" % hookname)
693+ with mkfile(hook_file) as f:
694+ print(content, file=f)
695+
696 def _setup_hooks_dir(self, preloads, hooks_dir=None):
697 if hooks_dir is None:
698 hooks_dir = self.temp_dir
699 preloads["click_get_hooks_dir"].side_effect = (
700 lambda: self.make_string(hooks_dir))
701+ self.hooks_dir = hooks_dir
702
703 def g_spawn_sync_side_effect(self, status_map, working_directory, argv,
704 envp, flags, child_setup, user_data,
705@@ -126,13 +151,12 @@
706 "click_get_hooks_dir") as (enter, preloads):
707 enter()
708 self._setup_hooks_dir(preloads)
709- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
710- print(dedent("""\
711+ self._make_hook_file(dedent("""\
712 Pattern: /usr/share/test/${id}.test
713 # Comment
714 Exec: test-update
715 User: root
716- """), file=f)
717+ """))
718 hook = Click.Hook.open(self.db, "test")
719 self.assertCountEqual(
720 ["pattern", "exec", "user"], hook.get_fields())
721@@ -146,8 +170,8 @@
722 "click_get_hooks_dir") as (enter, preloads):
723 enter()
724 self._setup_hooks_dir(preloads)
725- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
726- print("Pattern: /usr/share/test/${id}.test", file=f)
727+ self._make_hook_file(
728+ "Pattern: /usr/share/test/${id}.test")
729 hook = Click.Hook.open(self.db, "test")
730 self.assertEqual("test", hook.get_hook_name())
731
732@@ -156,9 +180,10 @@
733 "click_get_hooks_dir") as (enter, preloads):
734 enter()
735 self._setup_hooks_dir(preloads)
736- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
737- print("Pattern: /usr/share/test/${id}.test", file=f)
738- print("Hook-Name: other", file=f)
739+ self._make_hook_file(dedent("""\
740+ Pattern: /usr/share/test/${id}.test
741+ Hook-Name: other""")
742+ )
743 hook = Click.Hook.open(self.db, "test")
744 self.assertEqual("other", hook.get_hook_name())
745
746@@ -167,13 +192,12 @@
747 "click_get_hooks_dir") as (enter, preloads):
748 enter()
749 self._setup_hooks_dir(preloads)
750- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
751- print(dedent("""\
752+ self._make_hook_file(dedent("""\
753 Pattern: /usr/share/test/${id}.test
754 # Comment
755 Exec: test-update
756 User: root
757- """), file=f)
758+ """))
759 hook = Click.Hook.open(self.db, "test")
760 self.assertRaisesHooksError(
761 Click.HooksError.BAD_APP_NAME, hook.get_app_id,
762@@ -187,8 +211,8 @@
763 "click_get_hooks_dir") as (enter, preloads):
764 enter()
765 self._setup_hooks_dir(preloads)
766- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
767- print("Pattern: /usr/share/test/${short-id}.test", file=f)
768+ self._make_hook_file(
769+ "Pattern: /usr/share/test/${short-id}.test")
770 hook = Click.Hook.open(self.db, "test")
771 # It would perhaps be better if unrecognised $-expansions raised
772 # KeyError, but they don't right now.
773@@ -201,9 +225,9 @@
774 "click_get_hooks_dir") as (enter, preloads):
775 enter()
776 self._setup_hooks_dir(preloads)
777- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
778- print("Pattern: /usr/share/test/${short-id}.test", file=f)
779- print("Single-Version: yes", file=f)
780+ self._make_hook_file(dedent("""\
781+ Pattern: /usr/share/test/${short-id}.test
782+ Single-Version: yes"""))
783 hook = Click.Hook.open(self.db, "test")
784 self.assertEqual(
785 "/usr/share/test/package_app-name.test",
786@@ -231,8 +255,8 @@
787 "click_get_hooks_dir") as (enter, preloads):
788 enter()
789 self._setup_hooks_dir(preloads)
790- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
791- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
792+ self._make_hook_file(
793+ "Pattern: %s/${id}.test" % self.temp_dir)
794 os.makedirs(
795 os.path.join(self.temp_dir, "org.example.package", "1.0"))
796 hook = Click.Hook.open(self.db, "test")
797@@ -251,8 +275,8 @@
798 "click_get_hooks_dir") as (enter, preloads):
799 enter()
800 self._setup_hooks_dir(preloads)
801- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
802- print("Pattern: %s/${id}/" % self.temp_dir, file=f)
803+ self._make_hook_file(
804+ "Pattern: %s/${id}/" % self.temp_dir)
805 os.makedirs(
806 os.path.join(self.temp_dir, "org.example.package", "1.0"))
807 hook = Click.Hook.open(self.db, "test")
808@@ -276,8 +300,8 @@
809 "click_get_hooks_dir") as (enter, preloads):
810 enter()
811 self._setup_hooks_dir(preloads)
812- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
813- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
814+ self._make_hook_file(
815+ "Pattern: %s/${id}.test" % self.temp_dir)
816 underlay = os.path.join(self.temp_dir, "underlay")
817 overlay = os.path.join(self.temp_dir, "overlay")
818 db = Click.DB()
819@@ -304,8 +328,8 @@
820 "click_get_hooks_dir") as (enter, preloads):
821 enter()
822 self._setup_hooks_dir(preloads)
823- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
824- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
825+ self._make_hook_file(
826+ "Pattern: %s/${id}.test" % self.temp_dir)
827 symlink_path = os.path.join(
828 self.temp_dir, "org.example.package_test-app_1.0.test")
829 os.symlink("old-target", symlink_path)
830@@ -325,8 +349,8 @@
831 "click_get_hooks_dir") as (enter, preloads):
832 enter()
833 self._setup_hooks_dir(preloads)
834- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
835- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
836+ self._make_hook_file(
837+ "Pattern: %s/${id}.test" % self.temp_dir)
838 symlink_path = os.path.join(
839 self.temp_dir, "org.example.package_test-app_1.0.test")
840 os.symlink("old-target", symlink_path)
841@@ -341,28 +365,20 @@
842 enter()
843 self._setup_hooks_dir(
844 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
845- with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f:
846- print("Pattern: %s/${id}.new" % self.temp_dir, file=f)
847- with mkfile_utf8(os.path.join(
848- self.temp_dir, "test-1", "1.0", ".click", "info",
849- "test-1.manifest")) as f:
850- json.dump({
851+ self._make_hook_file(
852+ "Pattern: %s/${id}.new" % self.temp_dir,
853+ hookname="new")
854+ self._make_installed_click("test-1", "1.0", json_data={
855 "maintainer":
856 b"Unic\xc3\xb3de <unicode@example.org>".decode(
857 "UTF-8"),
858- "hooks": {"test1-app": {"new": "target-1"}},
859- }, f, ensure_ascii=False)
860- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
861- with mkfile_utf8(os.path.join(
862- self.temp_dir, "test-2", "2.0", ".click", "info",
863- "test-2.manifest")) as f:
864- json.dump({
865+ "hooks": {"test1-app": {"new": "target-1"}}})
866+ self._make_installed_click("test-2", "2.0", json_data={
867 "maintainer":
868 b"Unic\xc3\xb3de <unicode@example.org>".decode(
869 "UTF-8"),
870 "hooks": {"test1-app": {"new": "target-2"}},
871- }, f, ensure_ascii=False)
872- os.symlink("2.0", os.path.join(self.temp_dir, "test-2", "current"))
873+ })
874 hook = Click.Hook.open(self.db, "new")
875 hook.install(user_name=None)
876 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.new")
877@@ -382,22 +398,17 @@
878 enter()
879 self._setup_hooks_dir(
880 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
881- with mkfile(os.path.join(self.temp_dir, "hooks", "old.hook")) as f:
882- print("Pattern: %s/${id}.old" % self.temp_dir, file=f)
883- with mkfile(os.path.join(
884- self.temp_dir, "test-1", "1.0", ".click", "info",
885- "test-1.manifest")) as f:
886- json.dump({"hooks": {"test1-app": {"old": "target-1"}}}, f)
887- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
888+ self._make_hook_file(
889+ "Pattern: %s/${id}.old" % self.temp_dir,
890+ hookname="old")
891+ self._make_installed_click("test-1", "1.0", json_data={
892+ "hooks": {"test1-app": {"old": "target-1"}}})
893 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.old")
894 os.symlink(
895 os.path.join(self.temp_dir, "test-1", "1.0", "target-1"),
896 path_1)
897- with mkfile(os.path.join(
898- self.temp_dir, "test-2", "2.0", ".click", "info",
899- "test-2.manifest")) as f:
900- json.dump({"hooks": {"test2-app": {"old": "target-2"}}}, f)
901- os.symlink("2.0", os.path.join(self.temp_dir, "test-2", "current"))
902+ self._make_installed_click("test-2", "2.0", json_data={
903+ "hooks": {"test2-app": {"old": "target-2"}}})
904 path_2 = os.path.join(self.temp_dir, "test-2_test2-app_2.0.old")
905 os.symlink(
906 os.path.join(self.temp_dir, "test-2", "2.0", "target-2"),
907@@ -413,23 +424,14 @@
908 enter()
909 self._setup_hooks_dir(
910 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
911- with mkfile(os.path.join(
912- self.temp_dir, "hooks", "test.hook")) as f:
913- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
914- with mkfile(os.path.join(
915- self.temp_dir, "test-1", "1.0", ".click", "info",
916- "test-1.manifest")) as f:
917- json.dump({"hooks": {"test1-app": {"test": "target-1"}}}, f)
918- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
919- with mkfile(os.path.join(
920- self.temp_dir, "test-2", "1.0", ".click", "info",
921- "test-2.manifest")) as f:
922- json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f)
923- with mkfile(os.path.join(
924- self.temp_dir, "test-2", "1.1", ".click", "info",
925- "test-2.manifest")) as f:
926- json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f)
927- os.symlink("1.1", os.path.join(self.temp_dir, "test-2", "current"))
928+ self._make_hook_file(
929+ "Pattern: %s/${id}.test" % self.temp_dir)
930+ self._make_installed_click("test-1", "1.0", json_data={
931+ "hooks": {"test1-app": {"test": "target-1"}}})
932+ self._make_installed_click("test-2", "1.0", make_current=False,
933+ json_data={"hooks": {"test2-app": {"test": "target-2"}}})
934+ self._make_installed_click("test-2", "1.1", json_data={
935+ "hooks": {"test2-app": {"test": "target-2"}}})
936 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.test")
937 os.symlink(
938 os.path.join(self.temp_dir, "test-1", "1.0", "target-1"),
939@@ -465,13 +467,12 @@
940 "click_get_hooks_dir") as (enter, preloads):
941 enter()
942 self._setup_hooks_dir(preloads)
943- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
944- print(dedent("""\
945+ self._make_hook_file(dedent("""\
946 User-Level: yes
947 Pattern: ${home}/.local/share/test/${id}.test
948 # Comment
949 Exec: test-update
950- """), file=f)
951+ """))
952 hook = Click.Hook.open(self.db, "test")
953 self.assertCountEqual(
954 ["user-level", "pattern", "exec"], hook.get_fields())
955@@ -486,9 +487,10 @@
956 "click_get_hooks_dir") as (enter, preloads):
957 enter()
958 self._setup_hooks_dir(preloads)
959- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
960- print("User-Level: yes", file=f)
961- print("Pattern: ${home}/.local/share/test/${id}.test", file=f)
962+ self._make_hook_file(dedent("""\
963+ User-Level: yes
964+ Pattern: ${home}/.local/share/test/${id}.test""")
965+ )
966 hook = Click.Hook.open(self.db, "test")
967 self.assertEqual("test", hook.get_hook_name())
968
969@@ -497,10 +499,11 @@
970 "click_get_hooks_dir") as (enter, preloads):
971 enter()
972 self._setup_hooks_dir(preloads)
973- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
974- print("User-Level: yes", file=f)
975- print("Pattern: ${home}/.local/share/test/${id}.test", file=f)
976- print("Hook-Name: other", file=f)
977+ self._make_hook_file(dedent("""\
978+ User-Level: yes
979+ Pattern: ${home}/.local/share/test/${id}.test
980+ Hook-Name: other""")
981+ )
982 hook = Click.Hook.open(self.db, "test")
983 self.assertEqual("other", hook.get_hook_name())
984
985@@ -509,13 +512,12 @@
986 "click_get_hooks_dir") as (enter, preloads):
987 enter()
988 self._setup_hooks_dir(preloads)
989- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
990- print(dedent("""\
991+ self._make_hook_file(dedent("""\
992 User-Level: yes
993 Pattern: ${home}/.local/share/test/${id}.test
994 # Comment
995- Exec: test-update
996- """), file=f)
997+ Exec: test-update""")
998+ )
999 hook = Click.Hook.open(self.db, "test")
1000 self.assertRaisesHooksError(
1001 Click.HooksError.BAD_APP_NAME, hook.get_app_id,
1002@@ -531,11 +533,10 @@
1003 self._setup_hooks_dir(preloads)
1004 preloads["getpwnam"].side_effect = (
1005 lambda name: self.make_pointer(Passwd(pw_dir=b"/mock")))
1006- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1007- print("User-Level: yes", file=f)
1008- print(
1009- "Pattern: ${home}/.local/share/test/${short-id}.test",
1010- file=f)
1011+ self._make_hook_file(dedent("""\
1012+ User-Level: yes
1013+ Pattern: ${home}/.local/share/test/${short-id}.test
1014+ """))
1015 hook = Click.Hook.open(self.db, "test")
1016 self.assertEqual(
1017 "/mock/.local/share/test/package_app-name.test",
1018@@ -554,8 +555,8 @@
1019 print("Exec: test-update", file=f)
1020 hook = Click.Hook.open(self.db, "test")
1021 self.assertEqual(
1022- "test-user", hook.get_run_commands_user(user_name="test-user"))
1023- hook.run_commands(user_name="test-user")
1024+ self.TEST_USER, hook.get_run_commands_user(user_name=self.TEST_USER))
1025+ hook.run_commands(user_name=self.TEST_USER)
1026 self.assertEqual(
1027 [[b"/bin/sh", b"-c", b"test-update"]], self.spawn_calls)
1028
1029@@ -568,19 +569,20 @@
1030 preloads["click_get_user_home"].return_value = "/home/test-user"
1031 os.makedirs(os.path.join(
1032 self.temp_dir, "org.example.package", "1.0"))
1033- user_db = Click.User.for_user(self.db, "test-user")
1034+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1035 user_db.set_version("org.example.package", "1.0")
1036- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1037- print("User-Level: yes", file=f)
1038- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1039+ self._make_hook_file(dedent("""\
1040+ User-Level: yes
1041+ Pattern: %s/${id}.test""") % self.temp_dir
1042+ )
1043 hook = Click.Hook.open(self.db, "test")
1044 hook.install_package(
1045 "org.example.package", "1.0", "test-app", "foo/bar",
1046- user_name="test-user")
1047+ user_name=self.TEST_USER)
1048 symlink_path = os.path.join(
1049 self.temp_dir, "org.example.package_test-app_1.0.test")
1050 target_path = os.path.join(
1051- self.temp_dir, ".click", "users", "test-user",
1052+ self.temp_dir, ".click", "users", self.TEST_USER,
1053 "org.example.package", "foo", "bar")
1054 self.assertTrue(os.path.islink(symlink_path))
1055 self.assertEqual(target_path, os.readlink(symlink_path))
1056@@ -594,19 +596,20 @@
1057 preloads["click_get_user_home"].return_value = "/home/test-user"
1058 os.makedirs(os.path.join(
1059 self.temp_dir, "org.example.package", "1.0"))
1060- user_db = Click.User.for_user(self.db, "test-user")
1061+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1062 user_db.set_version("org.example.package", "1.0")
1063- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1064- print("User-Level: yes", file=f)
1065- print("Pattern: %s/${id}/" % self.temp_dir, file=f)
1066+ self._make_hook_file(dedent("""\
1067+ User-Level: yes
1068+ Pattern: %s/${id}/""") % self.temp_dir
1069+ )
1070 hook = Click.Hook.open(self.db, "test")
1071 hook.install_package(
1072 "org.example.package", "1.0", "test-app", "foo",
1073- user_name="test-user")
1074+ user_name=self.TEST_USER)
1075 symlink_path = os.path.join(
1076 self.temp_dir, "org.example.package_test-app_1.0")
1077 target_path = os.path.join(
1078- self.temp_dir, ".click", "users", "test-user",
1079+ self.temp_dir, ".click", "users", self.TEST_USER,
1080 "org.example.package", "foo")
1081 self.assertTrue(os.path.islink(symlink_path))
1082 self.assertEqual(target_path, os.readlink(symlink_path))
1083@@ -622,18 +625,19 @@
1084 self.temp_dir, "org.example.package", "1.0"))
1085 os.makedirs(os.path.join(
1086 self.temp_dir, "org.example.package", "1.1"))
1087- user_db = Click.User.for_user(self.db, "test-user")
1088+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1089 user_db.set_version("org.example.package", "1.0")
1090- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1091- print("User-Level: yes", file=f)
1092- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1093+ self._make_hook_file(dedent("""\
1094+ User-Level: yes
1095+ Pattern: %s/${id}.test""") % self.temp_dir
1096+ )
1097 hook = Click.Hook.open(self.db, "test")
1098 hook.install_package(
1099 "org.example.package", "1.0", "test-app", "foo/bar",
1100- user_name="test-user")
1101+ user_name=self.TEST_USER)
1102 hook.install_package(
1103 "org.example.package", "1.1", "test-app", "foo/bar",
1104- user_name="test-user")
1105+ user_name=self.TEST_USER)
1106 old_symlink_path = os.path.join(
1107 self.temp_dir, "org.example.package_test-app_1.0.test")
1108 symlink_path = os.path.join(
1109@@ -641,7 +645,7 @@
1110 self.assertFalse(os.path.islink(old_symlink_path))
1111 self.assertTrue(os.path.islink(symlink_path))
1112 target_path = os.path.join(
1113- self.temp_dir, ".click", "users", "test-user",
1114+ self.temp_dir, ".click", "users", self.TEST_USER,
1115 "org.example.package", "foo", "bar")
1116 self.assertEqual(target_path, os.readlink(symlink_path))
1117
1118@@ -657,17 +661,18 @@
1119 os.symlink("old-target", symlink_path)
1120 os.makedirs(os.path.join(
1121 self.temp_dir, "org.example.package", "1.0"))
1122- user_db = Click.User.for_user(self.db, "test-user")
1123+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1124 user_db.set_version("org.example.package", "1.0")
1125- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1126- print("User-Level: yes", file=f)
1127- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1128+ self._make_hook_file(dedent("""\
1129+ User-Level: yes
1130+ Pattern: %s/${id}.test""") % self.temp_dir
1131+ )
1132 hook = Click.Hook.open(self.db, "test")
1133 hook.install_package(
1134 "org.example.package", "1.0", "test-app", "foo/bar",
1135- user_name="test-user")
1136+ user_name=self.TEST_USER)
1137 target_path = os.path.join(
1138- self.temp_dir, ".click", "users", "test-user",
1139+ self.temp_dir, ".click", "users", self.TEST_USER,
1140 "org.example.package", "foo", "bar")
1141 self.assertTrue(os.path.islink(symlink_path))
1142 self.assertEqual(target_path, os.readlink(symlink_path))
1143@@ -679,16 +684,17 @@
1144 enter()
1145 self._setup_hooks_dir(preloads)
1146 preloads["click_get_user_home"].return_value = "/home/test-user"
1147- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1148- print("User-Level: yes", file=f)
1149- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1150+ self._make_hook_file(dedent("""\
1151+ User-Level: yes
1152+ Pattern: %s/${id}.test""") % self.temp_dir
1153+ )
1154 symlink_path = os.path.join(
1155 self.temp_dir, "org.example.package_test-app_1.0.test")
1156 os.symlink("old-target", symlink_path)
1157 hook = Click.Hook.open(self.db, "test")
1158 hook.remove_package(
1159 "org.example.package", "1.0", "test-app",
1160- user_name="test-user")
1161+ user_name=self.TEST_USER)
1162 self.assertFalse(os.path.exists(symlink_path))
1163
1164 def test_install(self):
1165@@ -696,32 +702,26 @@
1166 "click_get_hooks_dir", "click_get_user_home",
1167 ) as (enter, preloads):
1168 enter()
1169+ # Don't tell click about the hooks directory yet.
1170 self._setup_hooks_dir(preloads)
1171 preloads["click_get_user_home"].return_value = "/home/test-user"
1172 with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f:
1173 print("User-Level: yes", file=f)
1174 print("Pattern: %s/${id}.new" % self.temp_dir, file=f)
1175- user_db = Click.User.for_user(self.db, "test-user")
1176- with mkfile_utf8(os.path.join(
1177- self.temp_dir, "test-1", "1.0", ".click", "info",
1178- "test-1.manifest")) as f:
1179- json.dump({
1180+ self._make_installed_click("test-1", "1.0", json_data={
1181 "maintainer":
1182 b"Unic\xc3\xb3de <unicode@example.org>".decode(
1183 "UTF-8"),
1184 "hooks": {"test1-app": {"new": "target-1"}},
1185- }, f, ensure_ascii=False)
1186- user_db.set_version("test-1", "1.0")
1187- with mkfile_utf8(os.path.join(
1188- self.temp_dir, "test-2", "2.0", ".click", "info",
1189- "test-2.manifest")) as f:
1190- json.dump({
1191+ })
1192+ self._make_installed_click("test-2", "2.0", json_data={
1193 "maintainer":
1194 b"Unic\xc3\xb3de <unicode@example.org>".decode(
1195 "UTF-8"),
1196 "hooks": {"test1-app": {"new": "target-2"}},
1197- }, f, ensure_ascii=False)
1198- user_db.set_version("test-2", "2.0")
1199+ })
1200+ # Now tell click about the hooks directory and make sure it
1201+ # catches up correctly.
1202 self._setup_hooks_dir(
1203 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1204 hook = Click.Hook.open(self.db, "new")
1205@@ -730,14 +730,14 @@
1206 self.assertTrue(os.path.lexists(path_1))
1207 self.assertEqual(
1208 os.path.join(
1209- self.temp_dir, ".click", "users", "test-user", "test-1",
1210+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1211 "target-1"),
1212 os.readlink(path_1))
1213 path_2 = os.path.join(self.temp_dir, "test-2_test1-app_2.0.new")
1214 self.assertTrue(os.path.lexists(path_2))
1215 self.assertEqual(
1216 os.path.join(
1217- self.temp_dir, ".click", "users", "test-user", "test-2",
1218+ self.temp_dir, ".click", "users", self.TEST_USER, "test-2",
1219 "target-2"),
1220 os.readlink(path_2))
1221
1222@@ -747,17 +747,17 @@
1223 self.assertFalse(os.path.lexists(path_1))
1224 self.assertFalse(os.path.lexists(path_2))
1225
1226- hook.install(user_name="test-user")
1227+ hook.install(user_name=self.TEST_USER)
1228 self.assertTrue(os.path.lexists(path_1))
1229 self.assertEqual(
1230 os.path.join(
1231- self.temp_dir, ".click", "users", "test-user", "test-1",
1232+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1233 "target-1"),
1234 os.readlink(path_1))
1235 self.assertTrue(os.path.lexists(path_2))
1236 self.assertEqual(
1237 os.path.join(
1238- self.temp_dir, ".click", "users", "test-user", "test-2",
1239+ self.temp_dir, ".click", "users", self.TEST_USER, "test-2",
1240 "target-2"),
1241 os.readlink(path_2))
1242
1243@@ -766,29 +766,25 @@
1244 "click_get_hooks_dir", "click_get_user_home",
1245 ) as (enter, preloads):
1246 enter()
1247+ # Don't tell click about the hooks directory yet.
1248 self._setup_hooks_dir(preloads)
1249 preloads["click_get_user_home"].return_value = "/home/test-user"
1250 with mkfile(os.path.join(self.temp_dir, "hooks", "old.hook")) as f:
1251 print("User-Level: yes", file=f)
1252 print("Pattern: %s/${id}.old" % self.temp_dir, file=f)
1253- user_db = Click.User.for_user(self.db, "test-user")
1254- with mkfile(os.path.join(
1255- self.temp_dir, "test-1", "1.0", ".click", "info",
1256- "test-1.manifest")) as f:
1257- json.dump({"hooks": {"test1-app": {"old": "target-1"}}}, f)
1258- user_db.set_version("test-1", "1.0")
1259- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
1260+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1261+ self._make_installed_click("test-1", "1.0", json_data={
1262+ "hooks": {"test1-app": {"old": "target-1"}}})
1263 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.old")
1264 os.symlink(
1265 os.path.join(user_db.get_path("test-1"), "target-1"), path_1)
1266- with mkfile(os.path.join(
1267- self.temp_dir, "test-2", "2.0", ".click", "info",
1268- "test-2.manifest")) as f:
1269- json.dump({"hooks": {"test2-app": {"old": "target-2"}}}, f)
1270- user_db.set_version("test-2", "2.0")
1271+ self._make_installed_click("test-2", "2.0", json_data={
1272+ "hooks": {"test2-app": {"old": "target-2"}}})
1273 path_2 = os.path.join(self.temp_dir, "test-2_test2-app_2.0.old")
1274 os.symlink(
1275 os.path.join(user_db.get_path("test-2"), "target-2"), path_2)
1276+ # Now tell click about the hooks directory and make sure it
1277+ # catches up correctly.
1278 self._setup_hooks_dir(
1279 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1280 hook = Click.Hook.open(self.db, "old")
1281@@ -807,44 +803,37 @@
1282 os.path.join(self.temp_dir, "hooks", "test.hook")) as f:
1283 print("User-Level: yes", file=f)
1284 print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1285- user_db = Click.User.for_user(self.db, "test-user")
1286- with mkfile(os.path.join(
1287- self.temp_dir, "test-1", "1.0", ".click", "info",
1288- "test-1.manifest")) as f:
1289- json.dump({"hooks": {"test1-app": {"test": "target-1"}}}, f)
1290- user_db.set_version("test-1", "1.0")
1291- with mkfile(os.path.join(
1292- self.temp_dir, "test-2", "1.1", ".click", "info",
1293- "test-2.manifest")) as f:
1294- json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f)
1295- user_db.set_version("test-2", "1.1")
1296+ self._make_installed_click("test-1", "1.0", json_data={
1297+ "hooks": {"test1-app": {"test": "target-1"}}})
1298+ self._make_installed_click("test-2", "1.1", json_data={
1299+ "hooks": {"test2-app": {"test": "target-2"}}})
1300 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.test")
1301 os.symlink(
1302 os.path.join(
1303- self.temp_dir, ".click", "users", "test-user", "test-1",
1304+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1305 "target-1"),
1306 path_1)
1307 path_2 = os.path.join(self.temp_dir, "test-2_test2-app_1.1.test")
1308 path_3 = os.path.join(self.temp_dir, "test-3_test3-app_1.0.test")
1309 os.symlink(
1310 os.path.join(
1311- self.temp_dir, ".click", "users", "test-user", "test-3",
1312+ self.temp_dir, ".click", "users", self.TEST_USER, "test-3",
1313 "target-3"),
1314 path_3)
1315 self._setup_hooks_dir(
1316 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1317 hook = Click.Hook.open(self.db, "test")
1318- hook.sync(user_name="test-user")
1319+ hook.sync(user_name=self.TEST_USER)
1320 self.assertTrue(os.path.lexists(path_1))
1321 self.assertEqual(
1322 os.path.join(
1323- self.temp_dir, ".click", "users", "test-user", "test-1",
1324+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1325 "target-1"),
1326 os.readlink(path_1))
1327 self.assertTrue(os.path.lexists(path_2))
1328 self.assertEqual(
1329 os.path.join(
1330- self.temp_dir, ".click", "users", "test-user", "test-2",
1331+ self.temp_dir, ".click", "users", self.TEST_USER, "test-2",
1332 "target-2"),
1333 os.readlink(path_2))
1334 self.assertFalse(os.path.lexists(path_3))
1335@@ -859,18 +848,15 @@
1336 os.path.join(self.temp_dir, "hooks", "test.hook")) as f:
1337 print("User-Level: yes", file=f)
1338 print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1339- with mkfile(os.path.join(
1340- self.temp_dir, "test-package", "1.0", ".click", "info",
1341- "test-package.manifest")) as f:
1342- json.dump({"hooks": {"test-app": {"test": "target"}}}, f)
1343- all_users_db = Click.User.for_all_users(self.db)
1344- all_users_db.set_version("test-package", "1.0")
1345+ self._make_installed_click(
1346+ "test-package", "1.0", all_users=True, json_data={
1347+ "hooks": {"test-app": {"test": "target"}}})
1348 self._setup_hooks_dir(
1349 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1350 hook = Click.Hook.open(self.db, "test")
1351- hook.sync(user_name="test-user")
1352+ hook.sync(user_name=self.TEST_USER)
1353 self.assertFalse(os.path.exists(os.path.join(
1354- self.temp_dir, ".click", "users", "test-user",
1355+ self.temp_dir, ".click", "users", self.TEST_USER,
1356 "test-package")))
1357
1358 def test_sync_uses_deepest_copy(self):
1359@@ -911,7 +897,7 @@
1360 underlay_user_link = os.path.join(
1361 underlay, ".click", "users", "@all", "test-package")
1362 overlay_user_link = os.path.join(
1363- overlay, ".click", "users", "test-user", "test-package")
1364+ overlay, ".click", "users", self.TEST_USER, "test-package")
1365 Click.ensuredir(os.path.dirname(underlay_user_link))
1366 os.symlink(underlay_unpacked, underlay_user_link)
1367 Click.ensuredir(os.path.dirname(overlay_user_link))
1368@@ -922,7 +908,7 @@
1369 overlay_target_path = os.path.join(overlay_user_link, "foo")
1370 os.symlink(overlay_target_path, symlink_path)
1371 hook = Click.Hook.open(db, "test")
1372- hook.sync(user_name="test-user")
1373+ hook.sync(user_name=self.TEST_USER)
1374 self.assertTrue(os.path.islink(underlay_user_link))
1375 self.assertEqual(
1376 underlay_unpacked, os.readlink(underlay_user_link))
1377@@ -962,17 +948,9 @@
1378 yelp_other_path = os.path.join(
1379 self.temp_dir, "yelp", "other-test_app_1.0.txt")
1380 os.symlink("dummy", yelp_other_path)
1381- package_dir = os.path.join(self.temp_dir, "test")
1382- with mkfile(os.path.join(
1383- package_dir, "1.0", ".click", "info",
1384- "test.manifest")) as f:
1385- json.dump(
1386- {"hooks": {"app": {"yelp": "foo.txt", "unity": "foo.scope"}}},
1387- f)
1388- with mkfile(os.path.join(
1389- package_dir, "1.1", ".click", "info",
1390- "test.manifest")) as f:
1391- json.dump({}, f)
1392+ self._make_installed_click("test", "1.0", make_current=False, json_data={
1393+ "hooks": {"app": {"yelp": "foo.txt", "unity": "foo.scope"}}})
1394+ self._make_installed_click("test", "1.1", json_data={})
1395 Click.package_install_hooks(
1396 self.db, "test", "1.0", "1.1", user_name=None)
1397 self.assertFalse(os.path.lexists(unity_path))
1398@@ -995,15 +973,10 @@
1399 print("Hook-Name: b", file=f)
1400 os.mkdir(os.path.join(self.temp_dir, "a"))
1401 os.mkdir(os.path.join(self.temp_dir, "b"))
1402- package_dir = os.path.join(self.temp_dir, "test")
1403- with mkfile(os.path.join(
1404- package_dir, "1.0", ".click", "info",
1405- "test.manifest")) as f:
1406- json.dump({"hooks": {}}, f)
1407- with mkfile(os.path.join(
1408- package_dir, "1.1", ".click", "info",
1409- "test.manifest")) as f:
1410- json.dump({"hooks": {"app": {"a": "foo.a", "b": "foo.b"}}}, f)
1411+ self._make_installed_click("test", "1.0", make_current=False,
1412+ json_data={"hooks": {}})
1413+ self._make_installed_click("test", "1.1", json_data={
1414+ "hooks": {"app": {"a": "foo.a", "b": "foo.b"}}})
1415 Click.package_install_hooks(
1416 self.db, "test", "1.0", "1.1", user_name=None)
1417 self.assertTrue(os.path.lexists(
1418@@ -1113,3 +1086,95 @@
1419 self.assertFalse(os.path.lexists(unity_path))
1420 self.assertFalse(os.path.lexists(yelp_docs_path))
1421 self.assertFalse(os.path.lexists(yelp_other_path))
1422+
1423+
1424+class TestPackageHooksValidateFramework(TestClickHookBase):
1425+
1426+ def _setup_test_env(self, preloads):
1427+ preloads["click_get_user_home"].return_value = "/home/test-user"
1428+ self._setup_hooks_dir(
1429+ preloads, os.path.join(self.temp_dir, "hooks"))
1430+ self._make_hook_file(dedent("""\
1431+ User-Level: yes
1432+ Pattern: %s/${id}.test
1433+ """) % self.temp_dir)
1434+ self.hook_symlink_path = os.path.join(
1435+ self.temp_dir, "test-1_test1-app_1.0.test")
1436+
1437+ def test_links_are_kept_on_validate_framework(self):
1438+ with self.run_in_subprocess(
1439+ "click_get_hooks_dir", "click_get_user_home",
1440+ "click_get_frameworks_dir",
1441+ ) as (enter, preloads):
1442+ enter()
1443+ self._setup_frameworks(
1444+ preloads, frameworks=["ubuntu-sdk-13.10"])
1445+ self._setup_test_env(preloads)
1446+ self._make_installed_click(json_data={
1447+ "framework": "ubuntu-sdk-13.10",
1448+ "hooks": {
1449+ "test1-app": {"test": "target-1"}
1450+ },
1451+ })
1452+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1453+ # run the hooks
1454+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1455+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1456+
1457+ def test_links_are_kept_multiple_frameworks(self):
1458+ with self.run_in_subprocess(
1459+ "click_get_hooks_dir", "click_get_user_home",
1460+ "click_get_frameworks_dir",
1461+ ) as (enter, preloads):
1462+ enter()
1463+ self._setup_frameworks(
1464+ preloads, frameworks=["ubuntu-sdk-14.04", "ubuntu-sdk-13.10"])
1465+ self._setup_test_env(preloads)
1466+ self._make_installed_click(json_data={
1467+ "framework": "ubuntu-sdk-13.10",
1468+ "hooks": {
1469+ "test1-app": {"test": "target-1"}
1470+ },
1471+ })
1472+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1473+ # run the hooks
1474+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1475+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1476+
1477+ def test_links_are_removed_on_missing_framework(self):
1478+ with self.run_in_subprocess(
1479+ "click_get_hooks_dir", "click_get_user_home",
1480+ "click_get_frameworks_dir",
1481+ ) as (enter, preloads):
1482+ enter()
1483+ self._setup_frameworks(preloads, frameworks=["missing"])
1484+ self._setup_test_env(preloads)
1485+ self._make_installed_click(json_data={
1486+ "framework": "ubuntu-sdk-13.10",
1487+ "hooks": {
1488+ "test1-app": {"test": "target-1"}
1489+ },
1490+ })
1491+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1492+ # run the hooks
1493+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1494+ self.assertFalse(os.path.lexists(self.hook_symlink_path))
1495+
1496+ def test_links_are_removed_on_missing_multiple_framework(self):
1497+ with self.run_in_subprocess(
1498+ "click_get_hooks_dir", "click_get_user_home",
1499+ "click_get_frameworks_dir",
1500+ ) as (enter, preloads):
1501+ enter()
1502+ self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"])
1503+ self._setup_test_env(preloads)
1504+ self._make_installed_click(json_data={
1505+ "framework": "ubuntu-sdk-13.10, ubuntu-sdk-13.10-html",
1506+ "hooks": {
1507+ "test1-app": {"test": "target-1"}
1508+ },
1509+ })
1510+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1511+ # run the hooks
1512+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1513+ self.assertFalse(os.path.lexists(self.hook_symlink_path))
1514
1515=== modified file 'click/tests/test_install.py'
1516--- click/tests/test_install.py 2014-05-05 13:23:35 +0000
1517+++ click/tests/test_install.py 2014-05-20 13:13:48 +0000
1518@@ -43,7 +43,13 @@
1519 ClickInstallerPermissionDenied,
1520 )
1521 from click.preinst import static_preinst
1522-from click.tests.helpers import TestCase, mkfile, mock, touch
1523+from click.tests.helpers import (
1524+ disable_logging,
1525+ mkfile,
1526+ mock,
1527+ TestCase,
1528+ touch,
1529+)
1530
1531
1532 @contextmanager
1533@@ -103,14 +109,6 @@
1534 self.temp_dir, control_dir, data_dir, package_path)
1535 return package_path
1536
1537- def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]):
1538- frameworks_dir = self._create_mock_framework_dir(frameworks_dir)
1539- shutil.rmtree(frameworks_dir, ignore_errors=True)
1540- for framework in frameworks:
1541- self._create_mock_framework_file(framework)
1542- preloads["click_get_frameworks_dir"].side_effect = (
1543- lambda: self.make_string(frameworks_dir))
1544-
1545 def test_audit_no_click_version(self):
1546 path = self.make_fake_package()
1547 self.assertRaisesRegex(
1548@@ -234,6 +232,7 @@
1549 'Framework "missing" not present on system.*',
1550 ClickInstaller(self.db).audit, path)
1551
1552+ @disable_logging
1553 def test_audit_missing_framework_force(self):
1554 with self.run_in_subprocess(
1555 "click_get_frameworks_dir") as (enter, preloads):
1556@@ -659,6 +658,7 @@
1557 self.assertTrue(
1558 os.path.exists(os.path.join(root, "test-package", "current")))
1559
1560+ @disable_logging
1561 def test_reinstall_preinstalled(self):
1562 # Attempting to reinstall a preinstalled version shouldn't actually
1563 # reinstall it in an overlay database (which would cause
1564
1565=== modified file 'debian/changelog'
1566--- debian/changelog 2014-05-14 06:28:34 +0000
1567+++ debian/changelog 2014-05-20 13:13:48 +0000
1568@@ -1,3 +1,41 @@
1569+click (0.4.23.1) UNRELEASED; urgency=medium
1570+
1571+ * chroot: Force dpkg-architecture to recalculate everything rather than
1572+ picking up values from the environment, to avoid the test suite getting
1573+ confused by environment variables exported by dpkg-buildpackage.
1574+
1575+ -- Colin Watson <cjwatson@ubuntu.com> Tue, 20 May 2014 14:12:17 +0100
1576+
1577+click (0.4.23) utopic; urgency=medium
1578+
1579+ [ Michael Vogt ]
1580+ * Show human-readable error message when a click chroot subcommand fails
1581+ because of existing or non-existing chroots (LP: #1296820).
1582+ * Selectively disable logging on some tests to avoid message spam during
1583+ the test runs.
1584+ * When running hooks, remove hook symlinks if framework requirements are
1585+ not met (LP: #1271944).
1586+ * Cleanup the chroot if "click chroot create" fails (unless
1587+ --keep-broken-chroot is used)
1588+ * Fix sources.list generation when native_arch and target_arch are on the
1589+ same archive server (part of LP #1319153).
1590+ * Add "click framework list" command to list available frameworks
1591+ (LP: #1294659).
1592+
1593+ [ Pete Woods ]
1594+ * Add libunity-scopes-dev package to chroot (LP: #1320786).
1595+
1596+ [ Sergio Schvezov ]
1597+ * click chroot creation depends on dpkg-architecture, so recommend
1598+ dpkg-dev.
1599+
1600+ [ Colin Watson ]
1601+ * chroot: Handle the case where we can execute binaries for the target
1602+ architecture directly and thus don't need a cross-compiler
1603+ (LP: #1319153).
1604+
1605+ -- Colin Watson <cjwatson@ubuntu.com> Tue, 20 May 2014 14:10:11 +0100
1606+
1607 click (0.4.22) utopic; urgency=medium
1608
1609 [ Michael Vogt ]
1610
1611=== modified file 'debian/control'
1612--- debian/control 2014-03-13 13:56:57 +0000
1613+++ debian/control 2014-05-20 13:13:48 +0000
1614@@ -28,7 +28,7 @@
1615 Architecture: any
1616 Multi-Arch: foreign
1617 Depends: ${misc:Depends}, ${perl:Depends}, python3-click (= ${binary:Version})
1618-Recommends: debootstrap, schroot
1619+Recommends: debootstrap, schroot, dpkg-dev
1620 Description: build Click packages
1621 Click is a simplified packaging format that installs in a separate part of
1622 the file system, suitable for third-party applications.
1623
1624=== modified file 'doc/index.rst'
1625--- doc/index.rst 2014-05-08 12:49:11 +0000
1626+++ doc/index.rst 2014-05-20 13:13:48 +0000
1627@@ -38,8 +38,10 @@
1628 Then run::
1629
1630 $ ./autogen.sh
1631- $ ./configure --with-systemdsystemunitdir=/lib/systemd/system \
1632- --with-systemduserunitdir=/usr/lib/systemd/user
1633+ $ ./configure --prefix=/usr \
1634+ --sysconfdir=/etc \
1635+ --with-systemdsystemunitdir=/lib/systemd/system \
1636+ --with-systemduserunitdir=/usr/lib/systemd/user
1637 $ make
1638
1639 to build the project.
1640
1641=== modified file 'doc/manpage.rst'
1642--- doc/manpage.rst 2014-03-18 11:47:21 +0000
1643+++ doc/manpage.rst 2014-05-20 13:13:48 +0000
1644@@ -35,6 +35,7 @@
1645 click buildsource DIRECTORY
1646 click chroot
1647 click contents PATH
1648+ click framework list
1649 click hook install HOOK
1650 click hook remove HOOK
1651 click hook run-system
1652@@ -148,6 +149,11 @@
1653
1654 Display the contents of the Click package in PATH as a file listing.
1655
1656+click framework list
1657+--------------------
1658+
1659+Display a list of available frameworks as one framework per line.
1660+
1661 click hook install HOOK
1662 -----------------------
1663
1664
1665=== modified file 'lib/click/hooks.vala'
1666--- lib/click/hooks.vala 2014-04-08 09:31:40 +0000
1667+++ lib/click/hooks.vala 2014-05-20 13:13:48 +0000
1668@@ -56,9 +56,57 @@
1669 INCOMPLETE
1670 }
1671
1672+
1673+/* vala implementation of click.framework.validate_framework()
1674+ *
1675+ * Note that the required_frameworks string has the form
1676+ * framework1, framework2, ...
1677+ * See doc/file-format.rst for details.
1678+ */
1679+private bool
1680+validate_framework (string required_frameworks)
1681+{
1682+ // valid framework names, cf. debian policy ยง5.6.1
1683+ Regex valid_framework_re;
1684+ try {
1685+ valid_framework_re = new Regex ("^[a-z][a-z0-9.+-]+");
1686+ } catch (RegexError e) {
1687+ error ("Could not compile regex /^[a-z][a-z0-9.+-]+/: %s",
1688+ e.message);
1689+ }
1690+ var base_version = "";
1691+ foreach (var framework_name in required_frameworks.split (","))
1692+ {
1693+ framework_name = framework_name.strip ();
1694+ if (!valid_framework_re.match (framework_name))
1695+ return false;
1696+ Framework framework;
1697+ // now check the base-version
1698+ try {
1699+ framework = Framework.open (framework_name);
1700+ } catch (FrameworkError e) {
1701+ return false;
1702+ }
1703+ if (base_version == "")
1704+ base_version = framework.get_base_version ();
1705+ if (base_version != framework.get_base_version ())
1706+ return false;
1707+ }
1708+ return true;
1709+}
1710+
1711+private bool
1712+validate_framework_for_package (DB db, string package, string? version)
1713+{
1714+ var manifest = read_manifest (db, package, version);
1715+ if (!manifest.has_member ("framework"))
1716+ return true;
1717+ var required_frameworks = manifest.get_string_member ("framework");
1718+ return validate_framework (required_frameworks);
1719+}
1720+
1721 private Json.Object
1722-read_manifest_hooks (DB db, string package, string? version)
1723- throws DatabaseError
1724+read_manifest (DB db, string package, string? version)
1725 {
1726 if (version == null)
1727 return new Json.Object ();
1728@@ -69,15 +117,22 @@
1729 @"$package.manifest");
1730 parser.load_from_file (manifest_path);
1731 var manifest = parser.get_root ().get_object ();
1732- if (! manifest.has_member ("hooks"))
1733- return new Json.Object ();
1734- var hooks = manifest.get_object_member ("hooks");
1735- return hooks.ref ();
1736+ return manifest.ref ();
1737 } catch (Error e) {
1738 return new Json.Object ();
1739 }
1740 }
1741
1742+private Json.Object
1743+read_manifest_hooks (DB db, string package, string? version)
1744+{
1745+ var manifest = read_manifest (db, package, version);
1746+ if (! manifest.has_member ("hooks"))
1747+ return new Json.Object ();
1748+ var hooks = manifest.get_object_member ("hooks");
1749+ return hooks.ref ();
1750+}
1751+
1752 private class PreviousEntry : Object, Gee.Hashable<PreviousEntry> {
1753 public string path { get; construct; }
1754 public string package { get; construct; }
1755@@ -889,10 +944,16 @@
1756 var ret = new List<RelevantApp> ();
1757 var hook_name = get_hook_name ();
1758 foreach (var unpacked in get_all_packages (user_name)) {
1759- var manifest = read_manifest_hooks
1760+ // if the app is not using a valid framework (anymore)
1761+ // we don't consider it relevant (anymore)
1762+ if (!validate_framework_for_package
1763+ (db, unpacked.package, unpacked.version))
1764+ continue;
1765+
1766+ var manifest_hooks = read_manifest_hooks
1767 (db, unpacked.package, unpacked.version);
1768- foreach (var app_name in manifest.get_members ()) {
1769- var hooks = manifest.get_object_member
1770+ foreach (var app_name in manifest_hooks.get_members ()) {
1771+ var hooks = manifest_hooks.get_object_member
1772 (app_name);
1773 if (hooks.has_member (hook_name)) {
1774 var relative_path = hooks.get_string_member
1775@@ -960,6 +1021,7 @@
1776 unowned string package = app.package;
1777 unowned string version = app.version;
1778 unowned string app_name = app.app_name;
1779+
1780 seen.add (@"$(package)_$(app_name)_$(version)");
1781 if (is_user_level) {
1782 var user_db = new User.for_user
1783@@ -1016,7 +1078,7 @@
1784 * @new_version: The new version of the package.
1785 * @user_name: (allow-none): A user name, or null.
1786 *
1787- * Run hooks following removal of a Click package.
1788+ * Run hooks following install of a Click package.
1789 *
1790 * If @user_name is null, only run system-level hooks. If @user_name is not
1791 * null, only run user-level hooks for that user.

Subscribers

People subscribed via source and target branches

to all changes: