Merge lp:~townsend/libertine/locale-and-language into lp:libertine

Proposed by Christopher Townsend
Status: Merged
Approved by: Larry Price
Approved revision: 373
Merged at revision: 372
Proposed branch: lp:~townsend/libertine/locale-and-language
Merge into: lp:libertine
Diff against target: 361 lines (+109/-20)
8 files modified
python/libertine/ChrootContainer.py (+6/-2)
python/libertine/ContainersConfig.py (+6/-0)
python/libertine/HostInfo.py (+12/-0)
python/libertine/Libertine.py (+55/-10)
python/libertine/LxcContainer.py (+6/-2)
python/libertine/LxdContainer.py (+6/-2)
tools/libertine-container-manager (+17/-3)
tools/libertine-container-manager.1 (+1/-1)
To merge this branch: bzr merge lp:~townsend/libertine/locale-and-language
Reviewer Review Type Date Requested Status
Larry Price Approve
Libertine CI Bot continuous-integration Approve
Review via email: mp+314652@code.launchpad.net

Commit message

Set container's locale and language based on the host including installing necessary language packs.

Description of the change

Some notes about how this works:

- Locale will be set and necessary language packs installed during container creation to match what the host is set to.
- After a package installed, it will check for any supporting language packs to install based on what the current locale is set *in the container*.
- If a user changes their locale after a container has been created, the only way to update the container is to run 'l-c-m update' on the container.

To post a comment you must log in.
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
371. By Christopher Townsend

Fix mock tests.

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:371
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/320/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/637
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/519
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/519
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/519
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/519
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/647
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/628
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/628/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/628
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/628/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/628
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/628/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/628
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/628/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/320/rebuild

review: Approve (continuous-integration)
Revision history for this message
Larry Price (larryprice) wrote :

see inlines

Revision history for this message
Larry Price (larryprice) :
review: Needs Information
372. By Christopher Townsend

Some changes based on review.

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:372
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/322/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/639
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/521
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/521
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/521
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/521
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/649
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/630
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/630/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/630
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/630/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/630
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/630/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/630
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/630/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/322/rebuild

review: Approve (continuous-integration)
373. By Christopher Townsend

Forgot to add a paramater for ChrootContainer::update_packages()

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:373
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/324/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/641
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/523
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/523
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/523
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/523
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/651
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/632
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/632/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/632
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/632/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/632
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/632/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/632
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/632/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/324/rebuild

review: Approve (continuous-integration)
Revision history for this message
Larry Price (larryprice) wrote :

lgtm - this has the potential to make a lot of users very happy

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'python/libertine/ChrootContainer.py'
2--- python/libertine/ChrootContainer.py 2017-01-11 19:35:27 +0000
3+++ python/libertine/ChrootContainer.py 2017-01-13 18:36:04 +0000
4@@ -120,6 +120,8 @@
5
6 utils.create_libertine_user_data_dir(self.container_id)
7
8+ self.update_locale()
9+
10 if multiarch and self.architecture == 'amd64':
11 utils.get_logger().info("Adding i386 multiarch support...")
12 self.run_in_container("dpkg --add-architecture i386")
13@@ -146,10 +148,12 @@
14 # Check if the container was created as root and chown the user directories as necessary
15 chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id))
16
17+ super().create_libertine_container()
18+
19 return True
20
21- def update_packages(self):
22- retcode = super().update_packages()
23+ def update_packages(self, new_locale=None):
24+ retcode = super().update_packages(new_locale)
25 self._run_ldconfig()
26 return retcode == 0
27
28
29=== modified file 'python/libertine/ContainersConfig.py'
30--- python/libertine/ContainersConfig.py 2016-12-07 21:50:36 +0000
31+++ python/libertine/ContainersConfig.py 2017-01-13 18:36:04 +0000
32@@ -296,6 +296,12 @@
33 else:
34 return multiarch_support
35
36+ def update_container_locale(self, container_id, locale):
37+ self._set_value_by_key(container_id, 'locale', locale)
38+
39+ def get_container_locale(self, container_id):
40+ return self._get_value_by_key(container_id, 'locale')
41+
42 """
43 Operations for archive (PPA) maintenance in a Libertine container.
44 """
45
46=== modified file 'python/libertine/HostInfo.py'
47--- python/libertine/HostInfo.py 2016-11-21 17:18:55 +0000
48+++ python/libertine/HostInfo.py 2017-01-13 18:36:04 +0000
49@@ -12,6 +12,7 @@
50 # You should have received a copy of the GNU General Public License along
51 # with this program. If not, see <http://www.gnu.org/licenses/>.
52
53+import locale
54 import lsb_release
55 import os
56 import platform
57@@ -74,3 +75,14 @@
58 def get_host_timezone(self):
59 with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd:
60 return fd.read().strip('\n')
61+
62+ def get_host_locale(self):
63+ host_locale = locale.getlocale()
64+ full_locale = None
65+
66+ if len(host_locale) == 2:
67+ full_locale = "{}.{}".format(host_locale[0], host_locale[1])
68+ elif len(host_locale) == 1:
69+ full_locale = "{}".format(host_locale[0])
70+
71+ return full_locale
72
73=== modified file 'python/libertine/Libertine.py'
74--- python/libertine/Libertine.py 2017-01-03 17:51:30 +0000
75+++ python/libertine/Libertine.py 2017-01-13 18:36:04 +0000
76@@ -82,10 +82,15 @@
77
78 :param container_id: The machine-readable container name.
79 """
80- def __init__(self, container_id):
81+ def __init__(self, container_id, containers_config=None):
82+ if containers_config is None:
83+ containers_config = ContainersConfig()
84+
85 self.container_type = 'unknown'
86 self.container_id = container_id
87 self.root_path = libertine.utils.get_libertine_container_rootfs_path(self.container_id)
88+ self.locale = containers_config.get_container_locale(container_id)
89+ self.language = self._get_language_from_locale()
90 self.default_packages = ['matchbox-window-manager',
91 'libnss-extrausers',
92 'humanity-icon-theme',
93@@ -93,8 +98,37 @@
94 'maliit-inputcontext-gtk3',
95 'maliit-framework']
96
97+ def _get_language_from_locale(self):
98+ language = None
99+
100+ if self.locale is not None:
101+ language = self.locale.split('.')[0]
102+ if not language.startswith('zh_'):
103+ language = language.split('_')[0]
104+ elif language.startswith('zh_CN'):
105+ language = 'zh-hans'
106+ else:
107+ language = 'zh-hant'
108+
109+ return language
110+
111+ def check_language_support(self):
112+ if self.run_in_container("bash -c \"which check-language-support &> /dev/null\"") != 0:
113+ self.install_package('language-selector-common', update_cache=False)
114+
115+ self.run_in_container("bash -c \"{} install $(check-language-support -l {})\"".format(_apt_command_prefix(), self.language))
116+
117+ def update_locale(self):
118+ self.run_in_container("locale-gen {}".format(self.locale))
119+
120+ def install_base_language_packs(self):
121+ base_language_packs = ['language-pack-{}', 'language-pack-gnome-{}']
122+
123+ for language_pack in [p.format(self.language) for p in base_language_packs]:
124+ self.install_package(language_pack, update_cache=False)
125+
126 def create_libertine_container(self, password=None, multiarch=False):
127- pass
128+ self.install_base_language_packs()
129
130 def destroy_libertine_container(self):
131 pass
132@@ -150,11 +184,18 @@
133 """
134 return self.run_in_container(_apt_command_prefix() + 'update')
135
136- def update_packages(self):
137+ def update_packages(self, new_locale=None):
138 """
139 Updates all packages installed in the container.
140 """
141 self.update_apt_cache()
142+
143+ if new_locale:
144+ self.locale = new_locale
145+ self.language = self._get_language_from_locale()
146+ self.update_locale()
147+ self.install_base_language_packs()
148+
149 return self.run_in_container(_apt_command_prefix() + '--force-yes dist-upgrade') == 0
150
151 def install_package(self, package_name, no_dialog=False, update_cache=True):
152@@ -185,7 +226,11 @@
153 else:
154 if no_dialog:
155 os.environ['DEBIAN_FRONTEND'] = 'teletype'
156- return self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0
157+ ret = self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0
158+
159+ self.check_language_support()
160+
161+ return ret
162
163 def remove_package(self, package_name):
164 """
165@@ -254,8 +299,8 @@
166 """
167 A concrete mock container type. Used for unit testing.
168 """
169- def __init__(self, container_id):
170- super().__init__(container_id)
171+ def __init__(self, container_id, containers_config=None):
172+ super().__init__(container_id, containers_config)
173 self.container_type = "mock"
174
175 def create_libertine_container(self, password=None, multiarch=False):
176@@ -264,7 +309,7 @@
177 def destroy_libertine_container(self):
178 return True
179
180- def update_packages(self):
181+ def update_packages(self, new_locale=None):
182 return True
183
184 def install_package(self, package_name, no_dialog=False, update_cache=True):
185@@ -328,7 +373,7 @@
186 from libertine.ChrootContainer import LibertineChroot
187 self.container = LibertineChroot(container_id)
188 elif container_type == "mock":
189- self.container = LibertineMock(container_id)
190+ self.container = LibertineMock(container_id, self.containers_config)
191 else:
192 raise RuntimeError("Unsupported container type %s" % container_type)
193
194@@ -363,13 +408,13 @@
195
196 return self.container.create_libertine_container(password, multiarch)
197
198- def update_libertine_container(self):
199+ def update_libertine_container(self, new_locale=None):
200 """
201 Updates the contents of the container.
202 """
203 try:
204 with ContainerRunning(self.container):
205- return self.container.update_packages()
206+ return self.container.update_packages(new_locale)
207 except RuntimeError as e:
208 return handle_runtime_error(e)
209
210
211=== modified file 'python/libertine/LxcContainer.py'
212--- python/libertine/LxcContainer.py 2017-01-10 16:04:09 +0000
213+++ python/libertine/LxcContainer.py 2017-01-13 18:36:04 +0000
214@@ -189,14 +189,14 @@
215 cmd_args = shlex.split(command_string)
216 return self.container.attach_wait(lxc.attach_run_command, cmd_args)
217
218- def update_packages(self):
219+ def update_packages(self, update_locale=False):
220 if self.timezone_needs_update():
221 self.run_in_container("bash -c \'echo \"{}\" >/etc/timezone\'".format(
222 self.host_info.get_host_timezone()))
223 self.run_in_container("rm -f /etc/localtime")
224 self.run_in_container("dpkg-reconfigure -f noninteractive tzdata")
225
226- return super().update_packages()
227+ return super().update_packages(update_locale)
228
229 def destroy_libertine_container(self):
230 if not self.container.defined:
231@@ -260,6 +260,8 @@
232 self.destroy_libertine_container()
233 return False
234
235+ self.update_locale()
236+
237 self.run_in_container("userdel -r ubuntu")
238 self.run_in_container("useradd -u {} -p {} -G sudo {}".format(
239 str(user_id), crypt.crypt(password), str(username)))
240@@ -277,6 +279,8 @@
241 self.destroy_libertine_container()
242 return False
243
244+ super().create_libertine_container()
245+
246 utils.get_logger().info("stopping container ...")
247 self.stop_container()
248
249
250=== modified file 'python/libertine/LxdContainer.py'
251--- python/libertine/LxdContainer.py 2017-01-12 20:02:42 +0000
252+++ python/libertine/LxdContainer.py 2017-01-13 18:36:04 +0000
253@@ -269,6 +269,8 @@
254 self.destroy_libertine_container()
255 return False
256
257+ self.update_locale()
258+
259 username = os.environ['USER']
260 uid = str(os.getuid())
261 self.run_in_container("userdel -r ubuntu")
262@@ -294,16 +296,18 @@
263 self.destroy_libertine_container()
264 return False
265
266+ super().create_libertine_container()
267+
268 return True
269
270- def update_packages(self):
271+ def update_packages(self, update_locale=False):
272 if not self._timezone_in_sync():
273 utils.get_logger().info("Re-syncing timezones")
274 self.run_in_container("bash -c 'echo \"%s\" > /etc/timezone'" % self._host_info.get_host_timezone())
275 self.run_in_container("rm -f /etc/localtime")
276 self.run_in_container("dpkg-reconfigure -f noninteractive tzdata")
277
278- return super().update_packages()
279+ return super().update_packages(update_locale)
280
281 def destroy_libertine_container(self):
282 if not self._try_get_container():
283
284=== modified file 'tools/libertine-container-manager'
285--- tools/libertine-container-manager 2017-01-12 15:42:03 +0000
286+++ tools/libertine-container-manager 2017-01-13 18:36:04 +0000
287@@ -34,6 +34,14 @@
288 self.containers_config = ContainersConfig()
289 self.host_info = HostInfo()
290
291+ def _get_updated_locale(self, container_id):
292+ host_locale = self.host_info.get_host_locale()
293+
294+ if host_locale == self.containers_config.get_container_locale(container_id):
295+ return None
296+ else:
297+ return host_locale
298+
299 def create(self, args):
300 password = None
301
302@@ -88,6 +96,7 @@
303 self.containers_config.update_container_multiarch_support(args.id, multiarch)
304
305 try:
306+ self.containers_config.update_container_locale(args.id, self.host_info.get_host_locale())
307 container = LibertineContainer(args.id)
308 self.containers_config.update_container_install_status(args.id, "installing")
309 if not container.create_libertine_container(password, args.multiarch):
310@@ -142,7 +151,7 @@
311 else:
312 self.containers_config.add_new_package(container_id, package)
313
314- container = LibertineContainer(container_id)
315+ container = LibertineContainer(container_id, self.containers_config)
316
317 self.containers_config.update_package_install_status(container_id, package, "installing")
318 if not container.install_package(args.package, args.no_dialog):
319@@ -194,14 +203,18 @@
320
321 def update(self, args):
322 container_id = self.containers_config.check_container_id(args.id)
323+ new_locale = self._get_updated_locale(container_id)
324
325 container = LibertineContainer(container_id)
326
327 self.containers_config.update_container_install_status(container_id, "updating")
328- if not container.update_libertine_container():
329+ if not container.update_libertine_container(new_locale):
330 self.containers_config.update_container_install_status(container_id, "ready")
331 sys.exit(1)
332
333+ if new_locale:
334+ self.containers_config.update_container_locale(container_id, new_locale)
335+
336 self.containers_config.update_container_install_status(container_id, "ready")
337
338 def list(self, args):
339@@ -457,7 +470,8 @@
340 # Handle the update command and its options
341 parser_update = subparsers.add_parser(
342 'update',
343- help=("Update the packages in the Libertine container."))
344+ help=("Update the packages in the Libertine container. Also updates the container's "
345+ "locale and installs necessary language packs if the host's locale has changed."))
346 parser_update.add_argument(
347 '-i', '--id',
348 help=("Container identifier. Default container is used if omitted."))
349
350=== modified file 'tools/libertine-container-manager.1'
351--- tools/libertine-container-manager.1 2016-12-07 21:24:16 +0000
352+++ tools/libertine-container-manager.1 2017-01-13 18:36:04 +0000
353@@ -52,7 +52,7 @@
354 Searches the apt cache inside an existing Libertine container for matches based on the given search string.
355 .TP
356 .B libertine-container-manager update [options]
357-Updates the packages inside an existing Libertine container.
358+Updates the packages inside an existing Libertine container. Also updates the container's locale and installs necessary language packs if the host's locale has changed.
359 .TP
360 .B libertine-container-manager list [options]
361 Lists all existing Libertine containers.

Subscribers

People subscribed via source and target branches