Merge lp:~townsend/libertine/support-deb-install into lp:libertine

Proposed by Christopher Townsend
Status: Merged
Approved by: Stephen M. Webb
Approved revision: 181
Merged at revision: 186
Proposed branch: lp:~townsend/libertine/support-deb-install
Merge into: lp:libertine
Diff against target: 536 lines (+171/-58)
11 files modified
debian/control (+2/-0)
libertine/ContainerAppsList.cpp (+0/-7)
libertine/ContainerAppsList.h (+0/-3)
libertine/ContainerConfigList.cpp (+41/-17)
libertine/ContainerConfigList.h (+7/-5)
libertine/qml/AppAddView.qml (+20/-11)
libertine/qml/HomeView.qml (+13/-2)
libertine/qml/PackageInfoView.qml (+25/-0)
libertine/qml/SearchResults.qml (+0/-1)
python/libertine/Libertine.py (+46/-5)
tools/libertine-container-manager (+17/-7)
To merge this branch: bzr merge lp:~townsend/libertine/support-deb-install
Reviewer Review Type Date Requested Status
Stephen M. Webb (community) Approve
Libertine CI Bot continuous-integration Approve
Review via email: mp+287093@code.launchpad.net

Commit message

Support installing Debian packages located on the host into the Libertine container.
Also fix a couple of bugs found during testing.

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)
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)
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)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Stephen M. Webb (bregma) wrote :

When I tried to install a DEB file I ended up with a menu that did not have text [1]. I took a guess (and guessed right) and got the file selector page a selected a .deb file, but clicking 'Open' did nothing [2]. There was no way to navigate away from the file selector.

[1] http://i.imgur.com/TnfFe7M.png
[2] http://i.imgur.com/R6arHQ8.png

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
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)
181. By Christopher Townsend

Merge lp:libertine.

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Stephen M. Webb (bregma) wrote :

The file dialog is sorely missed but some alternative solution can be addressed later: the rest of the change seems to work OK.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2016-01-28 15:05:26 +0000
3+++ debian/control 2016-03-14 14:38:30 +0000
4@@ -10,6 +10,7 @@
5 libgtest-dev,
6 libpam0g-dev,
7 lsb-release,
8+ python3-apt,
9 python3-dev,
10 python3-distro-info,
11 python3-gi,
12@@ -37,6 +38,7 @@
13 Package: libertine-tools
14 Architecture: any
15 Depends: lsb-release,
16+ python3-apt,
17 python3-distro-info,
18 python3-libertine,
19 ${misc:Depends},
20
21=== modified file 'libertine/ContainerAppsList.cpp'
22--- libertine/ContainerAppsList.cpp 2016-01-20 17:34:19 +0000
23+++ libertine/ContainerAppsList.cpp 2016-03-14 14:38:30 +0000
24@@ -36,13 +36,6 @@
25 setContainerApps(QString const& container_id)
26 {
27 apps_ = container_config_list_->getAppsForContainer(container_id);
28-}
29-
30-
31-void ContainerAppsList::
32-addNewApp(QString const& container_id, QString const& package_name)
33-{
34- container_config_list_->addNewApp(container_id, package_name);
35
36 beginResetModel();
37 endResetModel();
38
39=== modified file 'libertine/ContainerAppsList.h'
40--- libertine/ContainerAppsList.h 2016-01-20 17:34:19 +0000
41+++ libertine/ContainerAppsList.h 2016-03-14 14:38:30 +0000
42@@ -59,9 +59,6 @@
43 setContainerApps(QString const& container_id);
44
45 Q_INVOKABLE void
46- addNewApp(QString const& container_id, QString const& package_name);
47-
48- Q_INVOKABLE void
49 removeApp();
50
51 Q_INVOKABLE bool
52
53=== modified file 'libertine/ContainerConfigList.cpp'
54--- libertine/ContainerConfigList.cpp 2016-03-10 21:33:25 +0000
55+++ libertine/ContainerConfigList.cpp 2016-03-14 14:38:30 +0000
56@@ -3,7 +3,7 @@
57 * @brief Libertine Manager list of containers configurations
58 */
59 /*
60- * Copyright 2015 Canonical Ltd
61+ * Copyright 2015-2016 Canonical Ltd
62 *
63 * Libertine is free software: you can redistribute it and/or modify it under
64 * the terms of the GNU General Public License, version 3, as published by the
65@@ -29,6 +29,7 @@
66 #include <QtCore/QJsonObject>
67 #include <QtCore/QJsonParseError>
68 #include <QtCore/QJsonValue>
69+#include <QtCore/QProcess>
70 #include <QtCore/QRegExp>
71 #include <QtCore/QSettings>
72 #include <QtCore/QString>
73@@ -114,20 +115,6 @@
74 }
75
76
77-void ContainerConfigList::
78-addNewApp(QString const& container_id, QString const& package_name)
79-{
80- for (auto const& config: configs_)
81- {
82- if (config->container_id() == container_id)
83- {
84- config->container_apps().append(new ContainerApps(package_name, CurrentStatus::New, this));
85- break;
86- }
87- }
88-}
89-
90-
91 QList<ContainerApps*> * ContainerConfigList::
92 getAppsForContainer(QString const& container_id)
93 {
94@@ -187,12 +174,49 @@
95 QString ContainerConfigList::
96 getAppVersion(QString const& app_info)
97 {
98- QStringList info = app_info.split('\n');
99+ if (app_info.startsWith("N:") || app_info.isEmpty())
100+ {
101+ return QString("Cannot determine package version.");
102+ }
103+ else
104+ {
105+ QStringList info = app_info.split('\n');
106
107- return info.at(1).section(": ", 1, 1);
108+ return info.at(1).section(": ", 1, 1);
109+ }
110 }
111
112
113+bool ContainerConfigList::
114+isValidDebianPackage(QString const& package_string)
115+{
116+ return (package_string.endsWith(".deb") &&
117+ QFile::exists(package_string));
118+}
119+
120+
121+QString ContainerConfigList::
122+getDebianPackageName(QString const& package_path)
123+{
124+ QProcess cmd;
125+ QString exec_line("dpkg-deb");
126+ QStringList args;
127+ QByteArray package_name;
128+
129+ args << "-f" << package_path << "Package";
130+
131+ cmd.start(exec_line, args);
132+
133+ if (!cmd.waitForStarted())
134+ return QString(package_name);
135+
136+ cmd.waitForFinished(-1);
137+
138+ package_name = cmd.readAllStandardOutput();
139+
140+ return QString(package_name.trimmed());
141+}
142+
143 QList<ContainerArchives*> * ContainerConfigList::
144 getArchivesForContainer(QString const& container_id)
145 {
146
147=== modified file 'libertine/ContainerConfigList.h'
148--- libertine/ContainerConfigList.h 2016-03-09 15:09:05 +0000
149+++ libertine/ContainerConfigList.h 2016-03-14 14:38:30 +0000
150@@ -3,7 +3,7 @@
151 * @brief Libertine Manager list of containers configurations
152 */
153 /*
154- * Copyright 2015 Canonical Ltd
155+ * Copyright 2015-2016 Canonical Ltd
156 *
157 * Libertine is free software: you can redistribute it and/or modify it under
158 * the terms of the GNU General Public License, version 3, as published by the
159@@ -95,10 +95,6 @@
160 Q_INVOKABLE void
161 deleteContainer();
162
163- Q_INVOKABLE void
164- addNewApp(QString const& container_id,
165- QString const& package_name);
166-
167 QList<ContainerApps*> *
168 getAppsForContainer(QString const& container_id);
169
170@@ -113,6 +109,12 @@
171 Q_INVOKABLE QString
172 getAppVersion(QString const& app_info);
173
174+ Q_INVOKABLE bool
175+ isValidDebianPackage(QString const& package_string);
176+
177+ Q_INVOKABLE QString
178+ getDebianPackageName(QString const& package_path);
179+
180 QList<ContainerArchives*> *
181 getArchivesForContainer(QString const& container_id);
182
183
184=== modified file 'libertine/qml/AppAddView.qml'
185--- libertine/qml/AppAddView.qml 2016-01-20 17:34:19 +0000
186+++ libertine/qml/AppAddView.qml 2016-03-14 14:38:30 +0000
187@@ -3,7 +3,7 @@
188 * @brief Libertine app add view
189 */
190 /*
191- * Copyright 2015 Canonical Ltd
192+ * Copyright 2015-2016 Canonical Ltd
193 *
194 * Libertine is free software: you can redistribute it and/or modify it under
195 * the terms of the GNU General Public License, version 3, as published by the
196@@ -18,7 +18,7 @@
197 */
198 import Libertine 1.0
199 import QtQuick 2.4
200-import QtQuick.Layouts 1.0
201+import QtQuick.Layouts 1.1
202 import Ubuntu.Components 1.2
203
204
205@@ -27,6 +27,7 @@
206 title: i18n.tr("Install Apps")
207 property var search_comp: null
208 property var search_obj: null
209+ property var install_signal: null
210
211 Label {
212 id: searchPackageMessage
213@@ -71,7 +72,7 @@
214 wrapMode: Text.Wrap
215 horizontalAlignment: Text.AlignHCenter
216
217- text: i18n.tr("Please enter the exact package name of the app to install:")
218+ text: i18n.tr("Please enter the exact package name or path to a Debian package to install:")
219 }
220
221 TextField {
222@@ -88,16 +89,19 @@
223 width: parent.width - anchors.margins * 2
224
225 onAccepted: {
226- if (!containerConfigList.isAppInstalled(mainView.currentContainer, text)) {
227- containerAppsList.addNewApp(mainView.currentContainer, text)
228+ var package_name = text
229+ if (containerConfigList.isValidDebianPackage(text)) {
230+ package_name = containerConfigList.getDebianPackageName(text)
231+ }
232+ if (!containerConfigList.isAppInstalled(mainView.currentContainer, package_name)) {
233 installPackage(text)
234 containerAppsList.setContainerApps(mainView.currentContainer)
235- mainView.currentPackage = text
236+ mainView.currentPackage = package_name
237 pageStack.pop()
238- pageStack.push(Qt.resolvedUrl("PackageInfoView.qml"))
239+ pageStack.push(Qt.resolvedUrl("PackageInfoView.qml"), {install_signal: install_signal})
240 }
241 else {
242- appInstallMessage.text = i18n.tr("Package ") + text + i18n.tr(" already installed. Please try a different package name.")
243+ appInstallMessage.text = i18n.tr("Package ") + package_name + i18n.tr(" already installed. Please try a different package name.")
244 appInstallMessage.visible = true
245 appName.text = ""
246 }
247@@ -130,8 +134,10 @@
248
249 head.actions: [
250 Action {
251- iconName: "search"
252- onTriggered: {
253+ iconName: "search"
254+ text: i18n.tr("Search for package")
255+ description: i18n.tr("Search for packages in archives based on the search string entered.")
256+ onTriggered: {
257 if (search_obj) {
258 search_obj.destroy()
259 packageListModel.clear()
260@@ -151,9 +157,11 @@
261 searchString.visible = true
262 searchString.forceActiveFocus()
263 }
264- },
265+ },
266 Action {
267 iconName: "settings"
268+ text: i18n.tr("Enter package name")
269+ description: i18n.tr("Enter the exact package name to install.")
270 onTriggered: {
271 if (search_obj) {
272 search_obj.destroy()
273@@ -183,6 +191,7 @@
274 "containerId": mainView.currentContainer,
275 "containerType": containerConfigList.getContainerType(mainView.currentContainer),
276 "data": package_name})
277+ install_signal = worker.finishedInstall
278 worker.start()
279 }
280
281
282=== modified file 'libertine/qml/HomeView.qml'
283--- libertine/qml/HomeView.qml 2016-02-10 19:31:10 +0000
284+++ libertine/qml/HomeView.qml 2016-03-14 14:38:30 +0000
285@@ -3,7 +3,7 @@
286 * @brief Libertine container apps view
287 */
288 /*
289- * Copyright 2015 Canonical Ltd
290+ * Copyright 2015-2016 Canonical Ltd
291 *
292 * Libertine is free software: you can redistribute it and/or modify it under
293 * the terms of the GNU General Public License, version 3, as published by the
294@@ -65,6 +65,18 @@
295 }
296 }
297
298+ Component.onCompleted: {
299+ containerConfigList.configChanged.connect(reloadAppList)
300+ }
301+
302+ Component.onDestruction: {
303+ containerConfigList.configChanged.disconnect(reloadAppList)
304+ }
305+
306+ function reloadAppList() {
307+ containerAppsList.setContainerApps(mainView.currentContainer)
308+ }
309+
310 UbuntuListView {
311 anchors.fill: parent
312 model: containerAppsList
313@@ -93,7 +105,6 @@
314 text: i18n.tr("info")
315 description: i18n.tr("Package Info")
316 onTriggered: {
317- containerAppsList.setContainerApps(mainView.currentContainer)
318 mainView.currentPackage = packageName
319 pageStack.push(Qt.resolvedUrl("PackageInfoView.qml"))
320 }
321
322=== modified file 'libertine/qml/PackageInfoView.qml'
323--- libertine/qml/PackageInfoView.qml 2016-01-22 17:27:03 +0000
324+++ libertine/qml/PackageInfoView.qml 2016-03-14 14:38:30 +0000
325@@ -28,8 +28,10 @@
326 property string currentContainer: mainView.currentContainer
327 property var currentPackage: mainView.currentPackage
328 property var statusText: containerConfigList.getAppStatus(currentContainer, currentPackage)
329+ property var failureReasonText: null
330 property var packageVersionText: i18n.tr("Obtaining package version...")
331 property var worker: null
332+ property var install_signal: null
333
334 Label {
335 id: packageVersion
336@@ -44,7 +46,18 @@
337 fontSize: "large"
338 }
339
340+ Label {
341+ id: failureReason
342+ anchors.top: packageStatus.bottom
343+ text: i18n.tr("Failure reason: ") + failureReasonText
344+ fontSize: "large"
345+ visible: false
346+ }
347+
348 Component.onCompleted: {
349+ if (install_signal) {
350+ install_signal.connect(installFinished)
351+ }
352 containerConfigList.configChanged.connect(reloadStatus)
353 var command = "apt-cache policy " + currentPackage
354 var comp = Qt.createComponent("ContainerManager.qml")
355@@ -59,6 +72,9 @@
356 Component.onDestruction: {
357 containerConfigList.configChanged.disconnect(reloadStatus)
358 worker.finishedCommand.disconnect(getPackageVersion)
359+ if (install_signal) {
360+ install_signal.disconnect(installFinished)
361+ }
362 }
363
364 function reloadStatus() {
365@@ -72,4 +88,13 @@
366 function getPackageVersion(command_output) {
367 packageVersionText = containerConfigList.getAppVersion(command_output)
368 }
369+
370+ function installFinished(success, error_msg) {
371+ if (!success) {
372+ statusText = i18n.tr("failed")
373+ failureReasonText = error_msg
374+ failureReason.visible = true
375+ }
376+ install_signal.disconnect(installFinished)
377+ }
378 }
379
380=== modified file 'libertine/qml/SearchResults.qml'
381--- libertine/qml/SearchResults.qml 2015-10-30 19:42:21 +0000
382+++ libertine/qml/SearchResults.qml 2016-03-14 14:38:30 +0000
383@@ -19,7 +19,6 @@
384 description: i18n.tr("Install Package")
385 onTriggered: {
386 if (!containerConfigList.isAppInstalled(mainView.currentContainer, model.package_name)) {
387- containerAppsList.addNewApp(mainView.currentContainer, model.package_name)
388 installPackage(model.package_name)
389 }
390 }
391
392=== modified file 'python/libertine/Libertine.py'
393--- python/libertine/Libertine.py 2016-03-09 15:09:05 +0000
394+++ python/libertine/Libertine.py 2016-03-14 14:38:30 +0000
395@@ -1,4 +1,4 @@
396-# Copyright 2015 Canonical Ltd.
397+# Copyright 2015-2106 Canonical Ltd.
398 #
399 # This program is free software: you can redistribute it and/or modify it
400 # under the terms of the GNU General Public License version 3, as published
401@@ -19,6 +19,7 @@
402 import json
403 import libertine.utils
404 import os
405+import shutil
406
407
408 def get_container_type(container_id):
409@@ -76,6 +77,29 @@
410 def destroy_libertine_container(self, verbosity=1):
411 pass
412
413+ def copy_package_to_container(self, package_path):
414+ """
415+ Copies a Debian package from the host to a pre-determined place
416+ in a container.
417+
418+ :param package_path: The full path to the Debian package located
419+ on the host.
420+ """
421+ if os.path.exists(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])):
422+ return False
423+
424+ shutil.copy2(package_path, os.path.join(self.root_path, 'tmp'))
425+ return True
426+
427+ def delete_package_in_container(self, package_path):
428+ """
429+ Deletes a previously copied in Debian package in a container.
430+
431+ :param package_path: The full path to the Debian package located
432+ on the host.
433+ """
434+ os.remove(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1]))
435+
436 def is_running(self):
437 """
438 Indicates if the container is 'running'. The definition of 'running'
439@@ -119,11 +143,27 @@
440 """
441 Installs a named package in the container.
442
443- :param package_name: The name of the package as APT understands it.
444+ :param package_name: The name of the package as APT understands it or
445+ a full path to a Debian package on the host.
446 :param verbosity: the chattiness of the output on a range from 0 to 2
447 """
448- return self.run_in_container(apt_command_prefix(verbosity) +
449- extra_apt_args + " install '" + package_name + "'") == 0
450+ if package_name.endswith('.deb'):
451+ delete_package = self.copy_package_to_container(package_name)
452+
453+ self.run_in_container('ls -la ' + os.environ['HOME'])
454+ self.run_in_container('dpkg -i ' +
455+ os.path.join('/', 'tmp', package_name.split('/')[-1]))
456+
457+ ret = self.run_in_container(apt_command_prefix(verbosity) +
458+ extra_apt_args + " install -f") == 0
459+
460+ if delete_package:
461+ self.delete_package_in_container(package_name)
462+
463+ return ret
464+ else:
465+ return self.run_in_container(apt_command_prefix(verbosity) +
466+ extra_apt_args + " install '" + package_name + "'") == 0
467
468 def configure_command(self, command, *args, verbosity=1):
469 """
470@@ -283,7 +323,8 @@
471 :param verbosity: The verbosity level of the progress reporting.
472 """
473 with ContainerRunning(self.container):
474- self.container.run_in_container(apt_command_prefix(verbosity) + "remove '" + package_name + "'") == 0
475+ self.container.run_in_container(apt_command_prefix(verbosity) + "purge '" + package_name + "'") == 0
476+ self.container.run_in_container(apt_command_prefix(verbosity) + "autoremove --purge") == 0
477
478 def search_package_cache(self, search_string):
479 """
480
481=== modified file 'tools/libertine-container-manager'
482--- tools/libertine-container-manager 2016-03-10 21:33:25 +0000
483+++ tools/libertine-container-manager 2016-03-14 14:38:30 +0000
484@@ -25,6 +25,7 @@
485 import os
486 import sys
487
488+from apt.debfile import DebPackage
489 from distro_info import UbuntuDistroInfo, DistroDataOutdated
490 from libertine import LibertineContainer
491
492@@ -353,20 +354,29 @@
493 elif not args.id:
494 args.id = get_default_container_id()
495
496- if package_exists(args.id, args.package):
497- print("Package \'%s\' is already installed." % args.package)
498+ if args.package.endswith('.deb'):
499+ if os.path.exists(args.package):
500+ package = DebPackage(args.package).pkgname
501+ else:
502+ print("%s does not exist." % args.package)
503+ sys.exit(1)
504+ else:
505+ package = args.package
506+
507+ if package_exists(args.id, package):
508+ print("Package \'%s\' is already installed." % package)
509 sys.exit(1)
510
511- add_new_package(args.id, args.package)
512+ add_new_package(args.id, package)
513
514 container = LibertineContainer(args.id)
515
516- update_package_install_status(args.id, args.package, "installing")
517+ update_package_install_status(args.id, package, "installing")
518 if not container.install_package(args.package, args.verbosity):
519- delete_package(args.id, args.package)
520+ delete_package(args.id, package)
521 sys.exit(1)
522
523- update_package_install_status(args.id, args.package, "installed")
524+ update_package_install_status(args.id, package, "installed")
525
526
527 def remove_package(args):
528@@ -541,7 +551,7 @@
529 parser_install.add_argument(
530 '-p', '--package',
531 required=True,
532- help=("Name of package to install. Required."))
533+ help=("Name of package to install or full path to a Debian package. Required."))
534 parser_install.add_argument(
535 '-i', '--id',
536 help=("Container identifier. Default container is used if omitted."))

Subscribers

People subscribed via source and target branches