Merge lp:~townsend/libertine/add-extra-archives into lp:libertine

Proposed by Christopher Townsend
Status: Merged
Approved by: Stephen M. Webb
Approved revision: 180
Merged at revision: 180
Proposed branch: lp:~townsend/libertine/add-extra-archives
Merge into: lp:libertine
Diff against target: 1149 lines (+692/-76)
17 files modified
libertine/ContainerArchivesList.cpp (+94/-0)
libertine/ContainerArchivesList.h (+81/-0)
libertine/ContainerConfig.cpp (+123/-17)
libertine/ContainerConfig.h (+40/-14)
libertine/ContainerConfigList.cpp (+15/-1)
libertine/ContainerConfigList.h (+4/-0)
libertine/ContainerManager.cpp (+10/-0)
libertine/ContainerManager.h (+1/-0)
libertine/libertine.cpp (+3/-0)
libertine/libertine.h (+2/-0)
libertine/qml/ConfigureContainer.qml (+34/-36)
libertine/qml/ExtraArchivesView.qml (+167/-0)
liblibertine/CMakeLists.txt (+1/-0)
python/libertine/ChrootContainer.py (+5/-2)
python/libertine/Libertine.py (+15/-4)
python/libertine/LxcContainer.py (+2/-0)
tools/libertine-container-manager (+95/-2)
To merge this branch: bzr merge lp:~townsend/libertine/add-extra-archives
Reviewer Review Type Date Requested Status
Stephen M. Webb (community) Approve
Libertine CI Bot continuous-integration Approve
Review via email: mp+288534@code.launchpad.net

Commit message

Enable feature to add and remove extra archives in the container. This is for PPA's only with this commit.

To post a comment you must log in.
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 :

Verified on desktop.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'libertine/ContainerArchivesList.cpp'
2--- libertine/ContainerArchivesList.cpp 1970-01-01 00:00:00 +0000
3+++ libertine/ContainerArchivesList.cpp 2016-03-10 21:34:32 +0000
4@@ -0,0 +1,94 @@
5+/**
6+ * @file ContainerArchivesList.cpp
7+ * @brief Libertine Manager list of extra container archives, ie, PPAs
8+ */
9+/*
10+ * Copyright 2016 Canonical Ltd
11+ *
12+ * Libertine is free software: you can redistribute it and/or modify it under
13+ * the terms of the GNU General Public License, version 3, as published by the
14+ * Free Software Foundation.
15+ *
16+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
17+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19+ *
20+ * You should have received a copy of the GNU General Public License
21+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
22+ */
23+#include "libertine/ContainerArchivesList.h"
24+#include "libertine/ContainerConfigList.h"
25+
26+ContainerArchivesList::
27+ContainerArchivesList(ContainerConfigList* container_config_list,
28+ QObject* parent)
29+: QAbstractListModel(parent)
30+, container_config_list_(container_config_list)
31+{ }
32+
33+
34+ContainerArchivesList::
35+~ContainerArchivesList()
36+{ }
37+
38+
39+void ContainerArchivesList::
40+setContainerArchives(QString const& container_id)
41+{
42+ archives_ = container_config_list_->getArchivesForContainer(container_id);
43+
44+ beginResetModel();
45+ endResetModel();
46+}
47+
48+
49+bool ContainerArchivesList::
50+empty() const noexcept
51+{ return archives_->empty(); }
52+
53+
54+ContainerArchivesList::size_type ContainerArchivesList::
55+size() const noexcept
56+{ return archives_->count(); }
57+
58+
59+int ContainerArchivesList::
60+rowCount(QModelIndex const&) const
61+{
62+ return this->size();
63+}
64+
65+
66+QHash<int, QByteArray> ContainerArchivesList::
67+roleNames() const
68+{
69+ QHash<int, QByteArray> roles;
70+ roles[static_cast<int>(DataRole::ArchiveName)] = "archiveName";
71+ roles[static_cast<int>(DataRole::ArchiveStatus)] = "archiveStatus";
72+
73+ return roles;
74+}
75+
76+
77+QVariant ContainerArchivesList::
78+data(QModelIndex const& index, int role) const
79+{
80+ QVariant result;
81+
82+ if (index.isValid() && index.row() <= archives_->count())
83+ {
84+ switch (static_cast<DataRole>(role))
85+ {
86+ case DataRole::ArchiveName:
87+ result = (*archives_)[index.row()]->archive_name();
88+ break;
89+ case DataRole::ArchiveStatus:
90+ result = (*archives_)[index.row()]->archive_status();
91+ break;
92+ case DataRole::Error:
93+ break;
94+ }
95+ }
96+
97+ return result;
98+}
99
100=== added file 'libertine/ContainerArchivesList.h'
101--- libertine/ContainerArchivesList.h 1970-01-01 00:00:00 +0000
102+++ libertine/ContainerArchivesList.h 2016-03-10 21:34:32 +0000
103@@ -0,0 +1,81 @@
104+/**
105+ * @file ContainerArchivesList.h
106+ * @brief Libertine Manager list of extra container archives (PPAs)
107+ */
108+/*
109+ * Copyright 2016 Canonical Ltd
110+ *
111+ * Libertine is free software: you can redistribute it and/or modify it under
112+ * the terms of the GNU General Public License, version 3, as published by the
113+ * Free Software Foundation.
114+ *
115+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
116+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
117+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
118+ *
119+ * You should have received a copy of the GNU General Public License
120+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
121+ */
122+#ifndef _CONTAINER_ARCHIVES_LIST_H_
123+#define _CONTAINER_ARCHIVES_LIST_H_
124+
125+#include "libertine/ContainerConfig.h"
126+
127+#include <QtCore/QAbstractListModel>
128+#include <QtCore/QList>
129+#include <QtCore/QObject>
130+#include <QtCore/QString>
131+
132+
133+class ContainerArchives;
134+class ContainerConfigList;
135+
136+class ContainerArchivesList
137+: public QAbstractListModel
138+{
139+ Q_OBJECT
140+
141+public:
142+ using ArchivesList = QList<ContainerArchives*>;
143+ using iterator = ArchivesList::iterator;
144+ using size_type = ArchivesList::size_type;
145+
146+ enum class DataRole
147+ : int
148+ {
149+ ArchiveName = Qt::UserRole + 1,
150+ ArchiveStatus,
151+ Error
152+ };
153+
154+public:
155+ explicit
156+ ContainerArchivesList(ContainerConfigList* container_config_list,
157+ QObject* parent = nullptr);
158+
159+ ~ContainerArchivesList();
160+
161+ Q_INVOKABLE void
162+ setContainerArchives(QString const& container_id);
163+
164+ Q_INVOKABLE bool
165+ empty() const noexcept;
166+
167+ size_type
168+ size() const noexcept;
169+
170+ int
171+ rowCount(QModelIndex const& parent = QModelIndex()) const;
172+
173+ QHash<int, QByteArray>
174+ roleNames() const;
175+
176+ QVariant
177+ data(QModelIndex const& index, int role = Qt::DisplayRole) const;
178+
179+private:
180+ ContainerConfigList* container_config_list_;
181+ ArchivesList* archives_;
182+};
183+
184+#endif /* _CONTAINER_ARCHIVES_LIST_H_ */
185
186=== modified file 'libertine/ContainerConfig.cpp'
187--- libertine/ContainerConfig.cpp 2016-02-10 19:31:10 +0000
188+++ libertine/ContainerConfig.cpp 2016-03-10 21:34:32 +0000
189@@ -178,15 +178,15 @@
190 return ContainerConfig::InstallStatus::New;
191 }
192
193- const static struct { QString string; ContainerApps::AppStatus enumeration; } app_status_names[] =
194+ const static struct { QString string; CurrentStatus enumeration; } status_names[] =
195 {
196- { QObject::tr("new"), ContainerApps::AppStatus::New },
197- { QObject::tr("installing"), ContainerApps::AppStatus::Installing },
198- { QObject::tr("installed"), ContainerApps::AppStatus::Installed },
199- { QObject::tr("failed"), ContainerApps::AppStatus::Failed },
200- { QObject::tr("removing"), ContainerApps::AppStatus::Removing },
201- { QObject::tr("removed"), ContainerApps::AppStatus::Removed },
202- { QString(), ContainerApps::AppStatus::New }
203+ { QObject::tr("new"), CurrentStatus::New },
204+ { QObject::tr("installing"), CurrentStatus::Installing },
205+ { QObject::tr("installed"), CurrentStatus::Installed },
206+ { QObject::tr("failed"), CurrentStatus::Failed },
207+ { QObject::tr("removing"), CurrentStatus::Removing },
208+ { QObject::tr("removed"), CurrentStatus::Removed },
209+ { QString(), CurrentStatus::New }
210 };
211
212 QString
213@@ -207,10 +207,10 @@
214 return package_name;
215 }
216
217- ContainerApps::AppStatus
218+ CurrentStatus
219 extract_app_status_from_json(QJsonObject const& json_object)
220 {
221- ContainerApps::AppStatus app_status = ContainerApps::AppStatus::New;
222+ CurrentStatus app_status = CurrentStatus::New;
223
224 QJsonValue value = json_object["appStatus"];
225 if (value != QJsonValue::Undefined)
226@@ -221,7 +221,7 @@
227 QString s = value.toString();
228 if (s.length() > 0)
229 {
230- for (auto const& name: app_status_names)
231+ for (auto const& name: status_names)
232 {
233 if (0 == s.compare(name.string, Qt::CaseInsensitive))
234 {
235@@ -241,7 +241,7 @@
236 {
237 QList<ContainerApps*> container_apps;
238 QString package_name;
239- ContainerApps::AppStatus app_status;
240+ CurrentStatus app_status;
241
242 QJsonArray installed_apps = json_object["installedApps"].toArray();
243
244@@ -255,12 +255,78 @@
245 }
246 return container_apps;
247 }
248+
249+ QString
250+ extract_archive_name_from_json(QJsonObject const& json_object)
251+ {
252+ QString archive_name;
253+
254+ QJsonValue value = json_object["archiveName"];
255+ if (value != QJsonValue::Undefined)
256+ {
257+ QJsonValue::Type value_type = value.type();
258+ if (value_type == QJsonValue::String)
259+ {
260+ archive_name = value.toString();
261+ }
262+ }
263+
264+ return archive_name;
265+ }
266+
267+ CurrentStatus
268+ extract_archive_status_from_json(QJsonObject const& json_object)
269+ {
270+ CurrentStatus archive_status = CurrentStatus::New;
271+
272+ QJsonValue value = json_object["archiveStatus"];
273+ if (value != QJsonValue::Undefined)
274+ {
275+ QJsonValue::Type value_type = value.type();
276+ if (value_type == QJsonValue::String)
277+ {
278+ QString s = value.toString();
279+ if (s.length() > 0)
280+ {
281+ for (auto const& name: status_names)
282+ {
283+ if (0 == s.compare(name.string, Qt::CaseInsensitive))
284+ {
285+ archive_status = name.enumeration;
286+ break;
287+ }
288+ }
289+ }
290+ }
291+ }
292+
293+ return archive_status;
294+ }
295+
296+ QList<ContainerArchives*>
297+ extract_container_archives_from_json(QJsonObject const& json_object)
298+ {
299+ QList<ContainerArchives*> container_archives;
300+ QString archive_name;
301+ CurrentStatus archive_status;
302+
303+ QJsonArray extra_archives = json_object["extraArchives"].toArray();
304+
305+ for (auto const& archive: extra_archives)
306+ {
307+ archive_name = extract_archive_name_from_json(archive.toObject());
308+ archive_status = extract_archive_status_from_json(archive.toObject());
309+
310+ container_archives.append(new ContainerArchives(archive_name, archive_status));
311+ }
312+ return container_archives;
313+ }
314 } // anonymous namespace
315
316
317 ContainerApps::
318 ContainerApps(QString const& package_name,
319- ContainerApps::AppStatus app_status,
320+ CurrentStatus app_status,
321 QObject* parent)
322 : QObject(parent)
323 , package_name_(package_name)
324@@ -280,7 +346,32 @@
325
326 QString const& ContainerApps::
327 app_status() const
328-{ return app_status_names[(int)app_status_].string; }
329+{ return status_names[(int)app_status_].string; }
330+
331+
332+ContainerArchives::
333+ContainerArchives(QString const& archive_name,
334+ CurrentStatus archive_status,
335+ QObject* parent)
336+: QObject(parent)
337+, archive_name_(archive_name)
338+, archive_status_(archive_status)
339+{ }
340+
341+
342+ContainerArchives::
343+~ContainerArchives()
344+{ }
345+
346+
347+QString const& ContainerArchives::
348+archive_name() const
349+{ return archive_name_; }
350+
351+
352+QString const& ContainerArchives::
353+archive_status() const
354+{ return status_names[(int)archive_status_].string; }
355
356
357 ContainerConfig::
358@@ -316,6 +407,7 @@
359 , multiarch_support_(extract_multiarch_support_from_json(json_object))
360 , install_status_(extract_install_status_from_json(json_object))
361 , container_apps_(extract_container_apps_from_json(json_object))
362+, container_archives_(extract_container_archives_from_json(json_object))
363 { }
364
365
366@@ -375,12 +467,19 @@
367 { return container_apps_; }
368
369
370+QList<ContainerArchives*> & ContainerConfig::
371+container_archives()
372+{ return container_archives_; }
373+
374+
375 QJsonObject ContainerConfig::
376 toJson() const
377 {
378 QJsonObject json_object,
379- app_object;
380- QJsonArray apps;
381+ app_object,
382+ archive_object;
383+ QJsonArray apps,
384+ archives;
385
386 json_object["id"] = container_id_;
387 json_object["name"] = container_name_;
388@@ -397,10 +496,17 @@
389 for (auto const& container_app: container_apps_)
390 {
391 app_object["packageName"] = container_app->package_name();
392- app_object["appStatus"] = app_status_names[0].string;
393+ app_object["appStatus"] = status_names[0].string;
394 apps.append(app_object);
395 }
396 json_object["installedApps"] = apps;
397
398+ for (auto const& container_archive: container_archives_)
399+ {
400+ archive_object["archiveName"] = container_archive->archive_name();
401+ archives.append(archive_object);
402+ }
403+ json_object["extraArchives"] = archives;
404+
405 return json_object;
406 }
407
408=== modified file 'libertine/ContainerConfig.h'
409--- libertine/ContainerConfig.h 2016-02-10 19:31:10 +0000
410+++ libertine/ContainerConfig.h 2016-03-10 21:34:32 +0000
411@@ -24,18 +24,17 @@
412 #include <QtCore/QString>
413
414
415+enum class CurrentStatus { New, Installing, Installed, Failed, Removing, Removed };
416+
417+
418 class ContainerApps
419 : public QObject
420 {
421 Q_OBJECT
422
423 public:
424- enum class AppStatus
425- { New, Installing, Installed, Failed, Removing, Removed };
426-
427-public:
428 ContainerApps(QString const& package_name,
429- AppStatus app_status,
430+ CurrentStatus app_status,
431 QObject* parent = nullptr);
432 ~ContainerApps();
433
434@@ -46,8 +45,31 @@
435 app_status() const;
436
437 private:
438- QString package_name_;
439- AppStatus app_status_;
440+ QString package_name_;
441+ CurrentStatus app_status_;
442+};
443+
444+
445+class ContainerArchives
446+: public QObject
447+{
448+ Q_OBJECT
449+
450+public:
451+ ContainerArchives(QString const& archive_name,
452+ CurrentStatus archive_status,
453+ QObject* parent = nullptr);
454+ ~ContainerArchives();
455+
456+ QString const&
457+ archive_name() const;
458+
459+ QString const&
460+ archive_status() const;
461+
462+private:
463+ QString archive_name_;
464+ CurrentStatus archive_status_;
465 };
466
467
468@@ -107,6 +129,9 @@
469 QList<ContainerApps*> &
470 container_apps();
471
472+ QList<ContainerArchives*> &
473+ container_archives();
474+
475 QJsonObject
476 toJson() const;
477
478@@ -115,13 +140,14 @@
479 void installStatusChanged();
480
481 private:
482- QString container_id_;
483- QString container_name_;
484- QString container_type_;
485- QString distro_series_;
486- QString multiarch_support_;
487- InstallStatus install_status_;
488- QList<ContainerApps*> container_apps_;
489+ QString container_id_;
490+ QString container_name_;
491+ QString container_type_;
492+ QString distro_series_;
493+ QString multiarch_support_;
494+ InstallStatus install_status_;
495+ QList<ContainerApps*> container_apps_;
496+ QList<ContainerArchives*> container_archives_;
497 };
498
499 #endif /* CONTAINER_CONTAINERCONFIG_H */
500
501=== modified file 'libertine/ContainerConfigList.cpp'
502--- libertine/ContainerConfigList.cpp 2016-02-11 15:25:09 +0000
503+++ libertine/ContainerConfigList.cpp 2016-03-10 21:34:32 +0000
504@@ -121,7 +121,7 @@
505 {
506 if (config->container_id() == container_id)
507 {
508- config->container_apps().append(new ContainerApps(package_name, ContainerApps::AppStatus::New, this));
509+ config->container_apps().append(new ContainerApps(package_name, CurrentStatus::New, this));
510 break;
511 }
512 }
513@@ -193,6 +193,20 @@
514 }
515
516
517+QList<ContainerArchives*> * ContainerConfigList::
518+getArchivesForContainer(QString const& container_id)
519+{
520+ for (auto const& config: configs_)
521+ {
522+ if (config->container_id() == container_id)
523+ {
524+ return &(config->container_archives());
525+ }
526+ }
527+ return nullptr;
528+}
529+
530+
531 QString ContainerConfigList::
532 getContainerType(QString const& container_id)
533 {
534
535=== modified file 'libertine/ContainerConfigList.h'
536--- libertine/ContainerConfigList.h 2016-02-11 15:25:09 +0000
537+++ libertine/ContainerConfigList.h 2016-03-10 21:34:32 +0000
538@@ -25,6 +25,7 @@
539
540
541 class ContainerApps;
542+class ContainerArchives;
543 class ContainerConfig;
544 class LibertineConfig;
545
546@@ -112,6 +113,9 @@
547 Q_INVOKABLE QString
548 getAppVersion(QString const& app_info);
549
550+ QList<ContainerArchives*> *
551+ getArchivesForContainer(QString const& container_id);
552+
553 Q_INVOKABLE QString
554 getContainerType(QString const& container_id);
555
556
557=== modified file 'libertine/ContainerManager.cpp'
558--- libertine/ContainerManager.cpp 2016-02-11 15:59:00 +0000
559+++ libertine/ContainerManager.cpp 2016-03-10 21:34:32 +0000
560@@ -395,6 +395,9 @@
561 void ContainerManagerWorker::
562 configureContainer(QStringList configure_command)
563 {
564+ QByteArray error_msg;
565+ bool result = true;
566+
567 QProcess libertine_cli_tool;
568 QString exec_line = libertine_container_manager_tool;
569 QStringList args;
570@@ -408,6 +411,13 @@
571
572 libertine_cli_tool.waitForFinished(-1);
573
574+ if (libertine_cli_tool.exitCode() != 0)
575+ {
576+ error_msg = libertine_cli_tool.readAllStandardOutput();
577+ result = false;
578+ }
579+
580+ emit finishedConfigure(result, QString(error_msg));
581 emit finished();
582 quit();
583 }
584
585=== modified file 'libertine/ContainerManager.h'
586--- libertine/ContainerManager.h 2016-02-11 16:25:49 +0000
587+++ libertine/ContainerManager.h 2016-03-10 21:34:32 +0000
588@@ -146,6 +146,7 @@
589 void finishedRemove(bool result, QString const& error_msg);
590 void finishedSearch(bool result, QList<QString> packageList);
591 void finishedCommand(QString const& command_output);
592+ void finishedConfigure(bool result, QString const& error_msg);
593 };
594
595 #endif /* CONTAINER_CONTAINERMANAGER_H_ */
596
597=== modified file 'libertine/libertine.cpp'
598--- libertine/libertine.cpp 2016-01-20 17:34:19 +0000
599+++ libertine/libertine.cpp 2016-03-10 21:34:32 +0000
600@@ -21,6 +21,7 @@
601 #include <cstdlib>
602 #include "libertine/ContainerManager.h"
603 #include "libertine/ContainerAppsList.h"
604+#include "libertine/ContainerArchivesList.h"
605 #include "libertine/ContainerConfig.h"
606 #include "libertine/ContainerConfigList.h"
607 #include "libertine/libertine.h"
608@@ -105,6 +106,7 @@
609
610 containers_ = new ContainerConfigList(config_.data(), this);
611 container_apps_ = new ContainerAppsList(containers_, this);
612+ container_archives_ = new ContainerArchivesList(containers_, this);
613 password_helper_ = new PasswordHelper();
614
615 initialize_view();
616@@ -131,6 +133,7 @@
617 QQmlContext* ctxt = view_.rootContext();
618 ctxt->setContextProperty("containerConfigList", containers_);
619 ctxt->setContextProperty("containerAppsList", container_apps_);
620+ ctxt->setContextProperty("containerArchivesList", container_archives_);
621 ctxt->setContextProperty("passwordHelper", password_helper_);
622
623 view_.setSource(QUrl::fromLocalFile(main_qml_source_file_));
624
625=== modified file 'libertine/libertine.h'
626--- libertine/libertine.h 2016-01-20 17:34:19 +0000
627+++ libertine/libertine.h 2016-03-10 21:34:32 +0000
628@@ -30,6 +30,7 @@
629 class LibertineConfig;
630 class PasswordHelper;
631 class ContainerAppsList;
632+class ContainerArchivesList;
633
634
635 class Libertine
636@@ -55,6 +56,7 @@
637 QFileSystemWatcher watcher_;
638 ContainerConfigList* containers_;
639 ContainerAppsList* container_apps_;
640+ ContainerArchivesList* container_archives_;
641 PasswordHelper* password_helper_;
642 QQuickView view_;
643 };
644
645=== modified file 'libertine/qml/ConfigureContainer.qml'
646--- libertine/qml/ConfigureContainer.qml 2016-02-11 15:25:09 +0000
647+++ libertine/qml/ConfigureContainer.qml 2016-03-10 21:34:32 +0000
648@@ -1,5 +1,5 @@
649 /**
650- * @file configureContainer.qml
651+ * @file ConfigureContainer.qml
652 * @brief Libertine configure container view
653 */
654 /*
655@@ -19,51 +19,49 @@
656 import Libertine 1.0
657 import QtQuick 2.4
658 import Ubuntu.Components 1.2
659+import Ubuntu.Components.ListItems 1.2 as ListItem
660
661
662 Page {
663 id: configureView
664 title: i18n.tr("Configure ") + mainView.currentContainer
665
666- Item {
667- visible: containerConfigList.getHostArchitecture() == 'x86_64' ? true : false
668- CheckBox {
669- id: multiarchCheckBox
670- anchors {
671- left: parent.left
672- top: parent.top
673- leftMargin: configureView.leftMargin
674- }
675- checked: containerConfigList.getContainerMultiarchSupport(mainView.currentContainer) == 'enabled' ? true : false
676- onClicked: {
677- var comp = Qt.createComponent("ContainerManager.qml")
678- if (multiarchCheckBox.checked) {
679- var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
680- "containerId": mainView.currentContainer,
681- "containerType": containerConfigList.getContainerType(mainView.currentContainer),
682- "data_list": ["--multiarch", "enable"]})
683- worker.start()
684+ Column {
685+ anchors.left: parent.left
686+ anchors.right: parent.right
687+
688+ ListItem.Standard {
689+ visible: containerConfigList.getHostArchitecture() == 'x86_64' ? true : false
690+ control: CheckBox {
691+ checked: containerConfigList.getContainerMultiarchSupport(mainView.currentContainer) == 'enabled' ? true : false
692+ onClicked: {
693+ var comp = Qt.createComponent("ContainerManager.qml")
694+ if (multiarchCheckBox.checked) {
695+ var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
696+ "containerId": mainView.currentContainer,
697+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
698+ "data_list": ["--multiarch", "enable"]})
699+ worker.start()
700+ }
701+ else {
702+ var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
703+ "containerId": mainView.currentContainer,
704+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
705+ "data_list": ["--multiarch", "disable"]})
706+ worker.start()
707+ }
708 }
709- else {
710- var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
711- "containerId": mainView.currentContainer,
712- "containerType": containerConfigList.getContainerType(mainView.currentContainer),
713- "data_list": ["--multiarch", "disable"]})
714- worker.start()
715- }
716- }
717- }
718- Label {
719- anchors {
720- left: multiarchCheckBox.right
721- right: parent.right
722- top: parent.bottom
723- verticalCenter: parent.verticalCenter
724- leftMargin: units.gu(2)
725- rightMargin: configureView.rightMargin
726 }
727 text: i18n.tr("i386 multiarch support")
728 }
729
730+ ListItem.SingleValue {
731+ text: i18n.tr("Additional archives and PPAs")
732+ progression: true
733+ onClicked: {
734+ containerArchivesList.setContainerArchives(mainView.currentContainer)
735+ pageStack.push(Qt.resolvedUrl("ExtraArchivesView.qml"))
736+ }
737+ }
738 }
739 }
740
741=== added file 'libertine/qml/ExtraArchivesView.qml'
742--- libertine/qml/ExtraArchivesView.qml 1970-01-01 00:00:00 +0000
743+++ libertine/qml/ExtraArchivesView.qml 2016-03-10 21:34:32 +0000
744@@ -0,0 +1,167 @@
745+/**
746+ * @file ExtraArchiveView.qml
747+ * @brief Libertine container add archive view
748+ */
749+/*
750+ * Copyright 2016 Canonical Ltd
751+ *
752+ * Libertine is free software: you can redistribute it and/or modify it under
753+ * the terms of the GNU General Public License, version 3, as published by the
754+ * Free Software Foundation.
755+ *
756+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
757+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
758+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
759+ *
760+ * You should have received a copy of the GNU General Public License
761+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
762+ */
763+import Libertine 1.0
764+import QtQuick 2.4
765+import Ubuntu.Components 1.2
766+import Ubuntu.Components.Popups 1.2
767+
768+Page {
769+ id: extraArchiveView
770+ title: i18n.tr("Additional Archives and PPAs")
771+ property var archive_name: null
772+ property var worker: null
773+
774+ head.actions: [
775+ Action {
776+ iconName: "add"
777+ text: i18n.tr("add")
778+ description: i18n.tr("Add a new PPA")
779+ onTriggered: PopupUtils.open(addArchivePopup)
780+ }
781+ ]
782+
783+ Component {
784+ id: addArchivePopup
785+ Dialog {
786+ id: addArchiveDialog
787+ title: i18n.tr("Add additional PPA")
788+ text: i18n.tr("Enter name of PPA in the form ppa:user/ppa-name:")
789+
790+ TextField {
791+ id: extraArchiveString
792+ onAccepted: {
793+ PopupUtils.close(addArchiveDialog)
794+ addArchive(text)
795+ }
796+ }
797+ Button {
798+ text: i18n.tr("OK")
799+ color: UbuntuColors.green
800+ onClicked: {
801+ PopupUtils.close(addArchiveDialog)
802+ addArchive(extraArchiveString.text)
803+ }
804+ }
805+ Button {
806+ text: i18n.tr("Cancel")
807+ color: UbuntuColors.red
808+ onClicked: PopupUtils.close(addArchiveDialog)
809+ }
810+ Component.onCompleted: {
811+ extraArchiveString.forceActiveFocus()
812+ }
813+ }
814+ }
815+
816+ UbuntuListView {
817+ id: extraArchiveList
818+ anchors.fill: parent
819+ model: containerArchivesList
820+ delegate: ListItem {
821+ ActivityIndicator {
822+ id: extraArchiveActivity
823+ anchors.verticalCenter: parent.verticalCenter
824+ visible: (archiveStatus === i18n.tr("installing") ||
825+ archiveStatus === i18n.tr("removing")) ? true : false
826+ running: extraArchiveActivity.visible
827+ }
828+ Label {
829+ anchors {
830+ verticalCenter: parent.verticalCenter
831+ left: extraArchiveActivity.running ? extraArchiveActivity.right : parent.left
832+ leftMargin: units.gu(2)
833+ }
834+ text: archiveName
835+ }
836+ leadingActions: ListItemActions {
837+ actions: [
838+ Action {
839+ iconName: "delete"
840+ text: i18n.tr("remove")
841+ description: i18n.tr("Remove extra archive")
842+ onTriggered: {
843+ deleteArchive(archiveName)
844+ }
845+ }
846+ ]
847+ }
848+ }
849+ }
850+
851+ function addArchive(archive) {
852+ var comp = Qt.createComponent("ContainerManager.qml")
853+ worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
854+ "containerId": mainView.currentContainer,
855+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
856+ "data_list": ["--add-archive", archive]})
857+ worker.finishedConfigure.connect(finishedConfigure)
858+ worker.start()
859+ }
860+
861+ function deleteArchive(archive) {
862+ var comp = Qt.createComponent("ContainerManager.qml")
863+ worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
864+ "containerId": mainView.currentContainer,
865+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
866+ "data_list": ["--delete-archive", archive]})
867+ worker.finishedConfigure.connect(finishedConfigure)
868+ worker.start()
869+ }
870+
871+ Component {
872+ id: addFailedPopup
873+
874+ Dialog {
875+ property var error_msg: null
876+ id: addFailedDialog
877+ title: i18n.tr("Adding archive failed")
878+ text: error_msg
879+
880+ Button {
881+ text: i18n.tr("Dismiss")
882+ onClicked: PopupUtils.close(addFailedDialog)
883+ }
884+ }
885+ }
886+
887+ Component.onCompleted: {
888+ containerConfigList.configChanged.connect(reloadArchives)
889+ }
890+
891+ Component.onDestruction: {
892+ containerConfigList.configChanged.disconnect(reloadArchives)
893+
894+ if (worker) {
895+ worker.finishedConfigure.disconnect(finishedConfigure)
896+ }
897+ }
898+
899+ function reloadArchives() {
900+ containerArchivesList.setContainerArchives(mainView.currentContainer)
901+ }
902+
903+ function finishedConfigure(result, error_msg) {
904+ if (result) {
905+ containerArchivesList.setContainerArchives(mainView.currentContainer)
906+ }
907+ else {
908+ PopupUtils.open(addFailedPopup, null, {'error_msg': error_msg})
909+ }
910+ }
911+}
912
913=== modified file 'liblibertine/CMakeLists.txt'
914--- liblibertine/CMakeLists.txt 2015-10-07 18:59:50 +0000
915+++ liblibertine/CMakeLists.txt 2016-03-10 21:34:32 +0000
916@@ -11,6 +11,7 @@
917 ${libertine_src}/ContainerConfig.cpp
918 ${libertine_src}/ContainerManager.cpp
919 ${libertine_src}/ContainerAppsList.cpp
920+ ${libertine_src}/ContainerArchivesList.cpp
921 ${libertine_src}/PasswordHelper.cpp
922 )
923 set_target_properties(libertine-common PROPERTIES
924
925=== modified file 'python/libertine/ChrootContainer.py'
926--- python/libertine/ChrootContainer.py 2016-02-08 16:47:18 +0000
927+++ python/libertine/ChrootContainer.py 2016-03-10 21:34:32 +0000
928@@ -96,7 +96,7 @@
929 fd.write("done\n")
930 os.fchmod(fd.fileno(), 0o755)
931
932- # Add universe and -updates to the chroot's sources.list
933+ # Add universe, multiverse, and -updates to the chroot's sources.list
934 if (utils.get_host_architecture() == 'armhf'):
935 archive = "deb http://ports.ubuntu.com/ubuntu-ports "
936 else:
937@@ -105,9 +105,11 @@
938 if verbosity == 1:
939 print("Updating chroot's sources.list entries...")
940 with open(os.path.join(self.root_path, 'etc', 'apt', 'sources.list'), 'a') as fd:
941+ fd.write(archive + installed_release + "-updates main\n")
942 fd.write(archive + installed_release + " universe\n")
943- fd.write(archive + installed_release + "-updates main\n")
944 fd.write(archive + installed_release + "-updates universe\n")
945+ fd.write(archive + installed_release + " multiverse\n")
946+ fd.write(archive + installed_release + "-updates multiverse\n")
947
948 utils.create_libertine_user_data_dir(self.container_id)
949
950@@ -155,6 +157,7 @@
951 print("Updating the contents of the container after creation...")
952 self.update_packages(verbosity)
953 self.install_package("libnss-extrausers", verbosity)
954+ self.install_package("software-properties-common", verbosity)
955
956 if verbosity == 1:
957 print("Installing Matchbox as the Xmir window manager...")
958
959=== modified file 'python/libertine/Libertine.py'
960--- python/libertine/Libertine.py 2016-02-10 19:31:10 +0000
961+++ python/libertine/Libertine.py 2016-03-10 21:34:32 +0000
962@@ -18,6 +18,7 @@
963 import contextlib
964 import json
965 import libertine.utils
966+import os
967
968
969 def get_container_type(container_id):
970@@ -133,10 +134,20 @@
971 """
972 if command == 'multiarch':
973 if args[0] == 'enable':
974- self.run_in_container("dpkg --add-architecture i386")
975+ return self.run_in_container("dpkg --add-architecture i386")
976 else:
977 self.run_in_container(apt_command_prefix(verbosity) + "purge \".*:i386\"")
978- self.run_in_container("dpkg --remove-architecture i386")
979+ return self.run_in_container("dpkg --remove-architecture i386")
980+
981+ elif command == 'add-archive':
982+ if not os.path.exists(os.path.join(self.root_path, 'usr', 'bin', 'add-apt-repository')):
983+ self.update_packages(verbosity)
984+ self.install_package("software-properties-common", verbosity)
985+
986+ return self.run_in_container("add-apt-repository -y " + args[0])
987+
988+ elif command == 'delete-archive':
989+ return self.run_in_container("add-apt-repository -y -r " + args[0])
990
991 def get_container_distro(self, container_id):
992 """
993@@ -323,8 +334,8 @@
994 :rtype: The output of the given command.
995 """
996 with ContainerRunning(self.container):
997- self.container.run_in_container(exec_line)
998+ return self.container.run_in_container(exec_line)
999
1000 def configure_command(self, command, *args):
1001 with ContainerRunning(self.container):
1002- self.container.configure_command(command, *args)
1003+ return self.container.configure_command(command, *args)
1004
1005=== modified file 'python/libertine/LxcContainer.py'
1006--- python/libertine/LxcContainer.py 2016-02-09 20:59:51 +0000
1007+++ python/libertine/LxcContainer.py 2016-03-10 21:34:32 +0000
1008@@ -189,6 +189,8 @@
1009 print("Updating the contents of the container after creation...")
1010 self.update_packages(verbosity)
1011
1012+ self.install_package("software-properties-common", verbosity)
1013+
1014 if verbosity == 1:
1015 print("Installing Matchbox as the Xmir window manager...")
1016 self.install_package('matchbox', verbosity=verbosity)
1017
1018=== modified file 'tools/libertine-container-manager'
1019--- tools/libertine-container-manager 2016-02-10 19:31:10 +0000
1020+++ tools/libertine-container-manager 2016-03-10 21:34:32 +0000
1021@@ -127,6 +127,64 @@
1022 return container['multiarch']
1023
1024
1025+def update_archive_install_status(container_id, archive_name, new_status):
1026+ container_list = read_container_config_file()
1027+
1028+ for container in container_list['containerList']:
1029+ if container['id'] == container_id:
1030+ for archive in container['extraArchives']:
1031+ if archive['archiveName'] == archive_name:
1032+ archive['archiveStatus'] = new_status
1033+ write_container_config_file(container_list)
1034+ return
1035+
1036+
1037+def add_container_archive(container_id, archive_name):
1038+ container_list = read_container_config_file()
1039+
1040+ for container in container_list['containerList']:
1041+ if container['id'] == container_id:
1042+ archive_obj = {'archiveName': archive_name, 'archiveStatus': 'new'}
1043+
1044+ if 'extraArchives' not in container:
1045+ container['extraArchives'] = [archive_obj]
1046+ else:
1047+ container['extraArchives'].append(archive_obj)
1048+
1049+ write_container_config_file(container_list)
1050+ break
1051+
1052+
1053+def delete_container_archive(container_id, archive_name):
1054+ container_list = read_container_config_file()
1055+
1056+ for container in container_list['containerList']:
1057+ if container['id'] == container_id:
1058+ for archive in container['extraArchives']:
1059+ if archive['archiveName'] == archive_name:
1060+ container['extraArchives'].remove(archive)
1061+ write_container_config_file(container_list)
1062+ return
1063+
1064+ print("%s does not exist." % archive_name)
1065+ sys.exit(1)
1066+
1067+
1068+def archive_exists(container_id, archive_name):
1069+ container_list = read_container_config_file()
1070+
1071+ for container in container_list['containerList']:
1072+ if container['id'] == container_id:
1073+ if 'extraArchives' not in container:
1074+ return False
1075+ else:
1076+ for archive in container['extraArchives']:
1077+ if archive['archiveName'] == archive_name:
1078+ return True
1079+
1080+ return False
1081+
1082+
1083 def add_new_container(id, name, type, distro):
1084 if not os.path.exists(libertine.utils.get_libertine_database_dir_path()):
1085 os.makedirs(libertine.utils.get_libertine_database_dir_path())
1086@@ -379,7 +437,7 @@
1087
1088 container = LibertineContainer(args.id)
1089
1090- container.exec_command(args.command)
1091+ sys.exit(container.exec_command(args.command))
1092
1093
1094 def configure(args):
1095@@ -399,11 +457,36 @@
1096 current_multiarch = get_container_multiarch_support(args.id)
1097 if current_multiarch == multiarch:
1098 print("i386 multiarch support is already %s" % multiarch)
1099- sys.exit(0)
1100+ sys.exit(1)
1101
1102 container.configure_command('multiarch', args.multiarch)
1103 update_container_multiarch_support(args.id, multiarch)
1104
1105+ elif args.add_archive:
1106+ if archive_exists(args.id, args.add_archive):
1107+ print("%s already added in container." % args.add_archive)
1108+ sys.exit(1)
1109+
1110+ add_container_archive(args.id, args.add_archive)
1111+ update_archive_install_status(args.id, args.add_archive, 'installing')
1112+ if container.configure_command('add-archive', args.add_archive) != 0:
1113+ delete_container_archive(args.id, args.add_archive)
1114+ sys.exit(1)
1115+
1116+ update_archive_install_status(args.id, args.add_archive, 'installed')
1117+
1118+
1119+ elif args.delete_archive:
1120+ if not archive_exists(args.id, args.delete_archive):
1121+ print("%s is not added in container." % args.delete_archive)
1122+ sys.exit(1)
1123+
1124+ update_archive_install_status(args.id, args.delete_archive, 'removing')
1125+ if container.configure_command('delete-archive', args.delete_archive) != 0:
1126+ sys.exit(1)
1127+
1128+ delete_container_archive(args.id, args.delete_archive)
1129+
1130
1131 if __name__ == '__main__':
1132 parser = argparse.ArgumentParser(description="Legacy X application support for Unity 8")
1133@@ -543,6 +626,16 @@
1134 help=("Enables or disables i386 multiarch support for amd64 Libertine "
1135 "containers. This option has no effect when the Libertine "
1136 "container is i386."))
1137+ parser_configure.add_argument(
1138+ '-a', '--add-archive',
1139+ metavar='Archive name',
1140+ help=("Adds an archive (PPA) in the specified Libertine container. Needs to be "
1141+ "in the form of \"ppa:user/ppa-name\"."))
1142+ parser_configure.add_argument(
1143+ '-d', '--delete-archive',
1144+ metavar='Archive name',
1145+ help=("Deletes an existing archive (PPA) in the specified Libertine container. "
1146+ "Needs to be in the form of \"ppa:user/ppa-name\"."))
1147 parser_configure.set_defaults(func=configure)
1148
1149 # Actually parse the args

Subscribers

People subscribed via source and target branches