Merge lp:~dobey/unity-scope-click/short-appid into lp:unity-scope-click
- short-appid
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | 473 |
Merged at revision: | 471 |
Proposed branch: | lp:~dobey/unity-scope-click/short-appid |
Merge into: | lp:unity-scope-click |
Prerequisite: | lp:~dobey/unity-scope-click/expect-false |
Diff against target: |
317 lines (+135/-76) 5 files modified
libclickscope/click/interface.cpp (+1/-0) libclickscope/click/preview.cpp (+56/-70) libclickscope/click/preview.h (+2/-3) libclickscope/tests/test_interface.cpp (+28/-0) libclickscope/tests/test_preview.cpp (+48/-3) |
To merge this branch: | bzr merge lp:~dobey/unity-scope-click/short-appid |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
unity-api-1-bot | continuous-integration | Approve | |
Charles Kerr (community) | Approve | ||
PS Jenkins bot | continuous-integration | Pending | |
Review via email: mp+298837@code.launchpad.net |
Commit message
Use shortened appid for click app launching url.
Description of the change
unity-api-1-bot (unity-api-1-bot) wrote : | # |
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:468
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:469
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:470
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:472
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Charles Kerr (charlesk) wrote : | # |
LGTM, optional suggestion inline
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:473
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'libclickscope/click/interface.cpp' |
2 | --- libclickscope/click/interface.cpp 2016-03-09 11:01:19 +0000 |
3 | +++ libclickscope/click/interface.cpp 2016-07-18 15:38:49 +0000 |
4 | @@ -212,6 +212,7 @@ |
5 | if (id.length() == 3) { |
6 | app.name = id[0].toUtf8().data(); |
7 | app.version = id[2].toUtf8().data(); |
8 | + app.url = "appid://" + id[0].toStdString() + "/" + id[1].toStdString() + "/current-user-version"; |
9 | } else { |
10 | app.name = "unknown"; |
11 | app.version = "unknown"; |
12 | |
13 | === modified file 'libclickscope/click/preview.cpp' |
14 | --- libclickscope/click/preview.cpp 2016-06-14 17:24:35 +0000 |
15 | +++ libclickscope/click/preview.cpp 2016-07-18 15:38:49 +0000 |
16 | @@ -52,6 +52,7 @@ |
17 | |
18 | #include <functional> |
19 | #include <iostream> |
20 | +#include <regex> |
21 | #include <sstream> |
22 | |
23 | #include <click/click-i18n.h> |
24 | @@ -945,47 +946,44 @@ |
25 | submit_future.get(); |
26 | } |
27 | } |
28 | - getApplicationUri(manifest, [this, reply, manifest, app_name, &review, userid, force_cache](const std::string& uri) { |
29 | - populateDetails([this, reply, uri, manifest, app_name](const PackageDetails &details){ |
30 | - cachedDetails = details; |
31 | - store_department(details); |
32 | - pushPackagePreviewWidgets(cachedWidgets, details, createButtons(uri, manifest)); |
33 | - }, |
34 | - [this, reply, &review, manifest, userid](const ReviewList& reviewlist, |
35 | - click::Reviews::Error error) { |
36 | - auto reviews = bring_to_front(reviewlist, userid); |
37 | - if (manifest.removable && !cachedDetails.download_url.empty()) { |
38 | - scopes::PreviewWidgetList review_input; |
39 | - bool has_reviewed = reviews.size() > 0 && reviews.front().reviewer_username == userid; |
40 | - |
41 | - Review existing_review; |
42 | - existing_review.id = 0; |
43 | - if (has_reviewed) { |
44 | - existing_review = reviews.front(); |
45 | - reviews.pop_front(); |
46 | - } |
47 | - review_input.push_back(createRatingWidget(existing_review)); |
48 | - cachedWidgets.push(review_input); |
49 | - cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, review_input); |
50 | - cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, review_input); |
51 | - } |
52 | - |
53 | - if (error == click::Reviews::Error::NoError) { |
54 | - auto const revs = reviewsWidgets(reviews); |
55 | - cachedWidgets.push(revs); |
56 | - cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, revs); |
57 | - cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, revs); |
58 | - } else { |
59 | - qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); |
60 | - } |
61 | - cachedWidgets.flush(reply); |
62 | - reply->finished(); |
63 | + populateDetails([this, reply, manifest, app_name](const PackageDetails &details){ |
64 | + cachedDetails = details; |
65 | + store_department(details); |
66 | + pushPackagePreviewWidgets(cachedWidgets, details, createButtons(manifest)); |
67 | + }, |
68 | + [this, reply, &review, manifest, userid](const ReviewList& reviewlist, |
69 | + click::Reviews::Error error) { |
70 | + auto reviews = bring_to_front(reviewlist, userid); |
71 | + if (manifest.removable && !cachedDetails.download_url.empty()) { |
72 | + scopes::PreviewWidgetList review_input; |
73 | + bool has_reviewed = reviews.size() > 0 && reviews.front().reviewer_username == userid; |
74 | + |
75 | + Review existing_review; |
76 | + existing_review.id = 0; |
77 | + if (has_reviewed) { |
78 | + existing_review = reviews.front(); |
79 | + reviews.pop_front(); |
80 | + } |
81 | + review_input.push_back(createRatingWidget(existing_review)); |
82 | + cachedWidgets.push(review_input); |
83 | + cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, review_input); |
84 | + cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, review_input); |
85 | + } |
86 | + |
87 | + if (error == click::Reviews::Error::NoError) { |
88 | + auto const revs = reviewsWidgets(reviews); |
89 | + cachedWidgets.push(revs); |
90 | + cachedWidgets.layout.appendToColumn(cachedWidgets.layout.singleColumn.column1, revs); |
91 | + cachedWidgets.layout.appendToColumn(cachedWidgets.layout.twoColumns.column1, revs); |
92 | + } else { |
93 | + qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); |
94 | + } |
95 | + cachedWidgets.flush(reply); |
96 | + reply->finished(); |
97 | }, force_cache); |
98 | - }); |
99 | } |
100 | |
101 | -scopes::PreviewWidgetList InstalledPreview::createButtons(const std::string& uri, |
102 | - const Manifest& manifest) |
103 | +scopes::PreviewWidgetList InstalledPreview::createButtons(const Manifest& manifest) |
104 | { |
105 | scopes::PreviewWidgetList widgets; |
106 | scopes::PreviewWidget buttons("buttons", "actions"); |
107 | @@ -993,6 +991,8 @@ |
108 | |
109 | std::string open_label = _("Open"); |
110 | |
111 | + auto uri = getApplicationUri(manifest); |
112 | + |
113 | if (!manifest.has_any_apps() && manifest.has_any_scopes()) { |
114 | open_label = _("Search"); |
115 | } |
116 | @@ -1030,43 +1030,29 @@ |
117 | return widgets; |
118 | } |
119 | |
120 | -void InstalledPreview::getApplicationUri(const Manifest& manifest, std::function<void(const std::string&)> callback) |
121 | +std::string InstalledPreview::getApplicationUri(const Manifest& manifest) |
122 | { |
123 | - QString app_url = QString::fromStdString(result.uri()); |
124 | - |
125 | - // asynchronously get application uri based on app name, if the uri is not application://. |
126 | - // this can happen if the app was just installed and we have its http uri from the Result. |
127 | - if (!app_url.startsWith("application:///")) { |
128 | - const std::string name = result["name"].get_string(); |
129 | - |
130 | + static std::regex appurl_match{"^(application|appid)://[a-zA-Z\\._/-]+$"}; |
131 | + |
132 | + if (!std::regex_match(result.uri(), appurl_match)) { |
133 | if (manifest.has_any_apps()) { |
134 | - qt::core::world::enter_with_task([this, name, callback] () |
135 | - { |
136 | - click::Interface().get_dotdesktop_filename(name, |
137 | - [callback, name] (std::string val, click::InterfaceError error) { |
138 | - std::string uri; |
139 | - if (error == click::InterfaceError::NoError) { |
140 | - uri = "application:///" + val; |
141 | - } else { |
142 | - qWarning() << "Can't get .desktop filename for" |
143 | - << QString::fromStdString(name); |
144 | - } |
145 | - callback(uri); |
146 | - } |
147 | - ); |
148 | - }); |
149 | + std::string uri = "appid://" + manifest.name + "/" + |
150 | + manifest.first_app_name + "/current-user-version"; |
151 | + return uri; |
152 | + } else if (manifest.has_any_scopes()) { |
153 | + unity::scopes::CannedQuery cq(manifest.first_scope_id); |
154 | + auto scope_uri = cq.to_uri(); |
155 | + qDebug() << "Found uri for scope" |
156 | + << QString::fromStdString(manifest.first_scope_id) |
157 | + << "-" << QString::fromStdString(scope_uri); |
158 | + return scope_uri; |
159 | } else { |
160 | - if (manifest.has_any_scopes()) { |
161 | - unity::scopes::CannedQuery cq(manifest.first_scope_id); |
162 | - auto scope_uri = cq.to_uri(); |
163 | - qDebug() << "Found uri for scope" << QString::fromStdString(manifest.first_scope_id) |
164 | - << "-" << QString::fromStdString(scope_uri); |
165 | - callback(scope_uri); |
166 | - } |
167 | + qWarning() << "Unable to find app or scope URI for:" |
168 | + << QString::fromStdString(manifest.name); |
169 | + return ""; |
170 | } |
171 | - } else { |
172 | - callback(result.uri()); |
173 | } |
174 | + return result.uri(); |
175 | } |
176 | |
177 | // class InstalledScopePreview |
178 | |
179 | === modified file 'libclickscope/click/preview.h' |
180 | --- libclickscope/click/preview.h 2016-06-01 13:36:55 +0000 |
181 | +++ libclickscope/click/preview.h 2016-07-18 15:38:49 +0000 |
182 | @@ -261,10 +261,9 @@ |
183 | |
184 | void run(unity::scopes::PreviewReplyProxy const& reply) override; |
185 | |
186 | - void getApplicationUri(const Manifest& manifest, std::function<void(const std::string&)> callback); |
187 | + std::string getApplicationUri(const Manifest& manifest); |
188 | std::string get_consumer_key(); |
189 | - scopes::PreviewWidgetList createButtons(const std::string& uri, |
190 | - const click::Manifest& manifest); |
191 | + scopes::PreviewWidgetList createButtons(const click::Manifest& manifest); |
192 | scopes::PreviewWidget createRatingWidget(const click::Review& review) const; |
193 | |
194 | private: |
195 | |
196 | === modified file 'libclickscope/tests/test_interface.cpp' |
197 | --- libclickscope/tests/test_interface.cpp 2016-03-07 14:10:22 +0000 |
198 | +++ libclickscope/tests/test_interface.cpp 2016-07-18 15:38:49 +0000 |
199 | @@ -207,6 +207,34 @@ |
200 | EXPECT_EQ(20, results.size()); |
201 | } |
202 | |
203 | +TEST_F(ClickInterfaceTest, testFindClockUsesShortAppid) |
204 | +{ |
205 | + QSharedPointer<click::KeyFileLocator> keyFileLocator( |
206 | + new click::KeyFileLocator( |
207 | + testing::systemApplicationsDirectoryForTesting(), |
208 | + testing::userApplicationsDirectoryForTesting())); |
209 | + |
210 | + click::Interface iface(keyFileLocator); |
211 | + |
212 | + auto results = iface.find_installed_apps("Clock"); |
213 | + EXPECT_EQ(1u, results.size()); |
214 | + EXPECT_EQ("appid://com.ubuntu.clock/clock/current-user-version", results.begin()->url); |
215 | +} |
216 | + |
217 | +TEST_F(ClickInterfaceTest, testFindLegacyAppUsesDeskopId) |
218 | +{ |
219 | + QSharedPointer<click::KeyFileLocator> keyFileLocator( |
220 | + new click::KeyFileLocator( |
221 | + testing::systemApplicationsDirectoryForTesting(), |
222 | + testing::userApplicationsDirectoryForTesting())); |
223 | + |
224 | + click::Interface iface(keyFileLocator); |
225 | + |
226 | + auto results = iface.find_installed_apps("Messaging"); |
227 | + EXPECT_EQ(1u, results.size()); |
228 | + EXPECT_EQ("application:///messaging-app.desktop", results.begin()->url); |
229 | +} |
230 | + |
231 | // |
232 | // test that application with a default department id key in the desktop |
233 | // file is returned when department matches |
234 | |
235 | === modified file 'libclickscope/tests/test_preview.cpp' |
236 | --- libclickscope/tests/test_preview.cpp 2016-06-01 13:36:55 +0000 |
237 | +++ libclickscope/tests/test_preview.cpp 2016-07-18 15:38:49 +0000 |
238 | @@ -575,7 +575,9 @@ |
239 | .WillOnce(Return(true)); |
240 | click::Manifest manifest; |
241 | manifest.removable = true; |
242 | - auto widgets = preview.createButtons("fake uri", manifest); |
243 | + manifest.name = "fake"; |
244 | + manifest.first_app_name = "fake"; |
245 | + auto widgets = preview.createButtons(manifest); |
246 | ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); |
247 | ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "cancel_purchase_installed"); |
248 | } |
249 | @@ -585,7 +587,9 @@ |
250 | EXPECT_CALL(preview, isRefundable()).Times(0); |
251 | click::Manifest manifest; |
252 | manifest.removable = true; |
253 | - auto widgets = preview.createButtons("fake uri", manifest); |
254 | + manifest.name = "fake"; |
255 | + manifest.first_app_name = "fake"; |
256 | + auto widgets = preview.createButtons(manifest); |
257 | ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); |
258 | ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "uninstall_click"); |
259 | } |
260 | @@ -595,8 +599,10 @@ |
261 | EXPECT_CALL(preview, isRefundable()).Times(0); |
262 | click::Manifest manifest; |
263 | manifest.removable = true; |
264 | + manifest.name = "fake"; |
265 | + manifest.first_app_name = "fake"; |
266 | click::PackageDetails details; |
267 | - auto widgets = preview.createButtons("fake uri", manifest); |
268 | + auto widgets = preview.createButtons(manifest); |
269 | ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); |
270 | ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "uninstall_click"); |
271 | } |
272 | @@ -620,6 +626,45 @@ |
273 | ASSERT_EQ(widget.widget_type(), "rating-edit"); |
274 | } |
275 | |
276 | +TEST_F(InstalledPreviewTest, testGetApplicationURIClick) { |
277 | + click::InstalledPreview preview(result, metadata, client, pay_package, depts); |
278 | + click::Manifest manifest; |
279 | + manifest.name = "com.ubuntu.clock"; |
280 | + manifest.first_app_name = "clock"; |
281 | + ASSERT_EQ(preview.getApplicationUri(manifest), "appid://com.ubuntu.clock/clock/current-user-version"); |
282 | +} |
283 | + |
284 | +TEST_F(InstalledPreviewTest, testGetApplicationURIClickInResult) { |
285 | + std::string uri{"appid://com.ubuntu.clock/clock/current-user-version"}; |
286 | + result.set_uri(uri); |
287 | + click::InstalledPreview preview(result, metadata, client, pay_package, depts); |
288 | + click::Manifest manifest; |
289 | + ASSERT_EQ(preview.getApplicationUri(manifest), uri); |
290 | +} |
291 | + |
292 | +TEST_F(InstalledPreviewTest, testGetApplicationURINonClick) { |
293 | + std::string uri{"application:///web-browser.desktop"}; |
294 | + result.set_uri(uri); |
295 | + click::InstalledPreview preview(result, metadata, client, pay_package, depts); |
296 | + click::Manifest manifest; |
297 | + ASSERT_EQ(preview.getApplicationUri(manifest), uri); |
298 | +} |
299 | + |
300 | +TEST_F(InstalledPreviewTest, testGetApplicationURIScope) { |
301 | + click::InstalledPreview preview(result, metadata, client, pay_package, depts); |
302 | + click::Manifest manifest; |
303 | + manifest.name = "com.ubuntu.nearby"; |
304 | + manifest.first_scope_id = "nearby"; |
305 | + ASSERT_EQ(preview.getApplicationUri(manifest), "scope://nearby?q="); |
306 | +} |
307 | + |
308 | +TEST_F(InstalledPreviewTest, testGetApplicationURINotAppOrScope) { |
309 | + result.set_uri("https://foo.com"); |
310 | + click::InstalledPreview preview(result, metadata, client, pay_package, depts); |
311 | + click::Manifest manifest; |
312 | + ASSERT_EQ(preview.getApplicationUri(manifest), ""); |
313 | +} |
314 | + |
315 | class FakeCancelPurchasePreview : public click::CancelPurchasePreview { |
316 | public: |
317 | FakeCancelPurchasePreview(const unity::scopes::Result& result, bool installed) |
PASSED: Continuous integration, rev:467 /jenkins. canonical. com/unity- api-1/job/ lp-unity- scope-click- ci/12/ /jenkins. canonical. com/unity- api-1/job/ build/103 /jenkins. canonical. com/unity- api-1/job/ build-0- fetch/110 /jenkins. canonical. com/unity- api-1/job/ build-1- sourcepkg/ release= vivid+overlay/ 67 /jenkins. canonical. com/unity- api-1/job/ build-1- sourcepkg/ release= xenial/ 67 /jenkins. canonical. com/unity- api-1/job/ build-1- sourcepkg/ release= yakkety/ 67 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial/ 43/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= yakkety/ 43 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= yakkety/ 43/artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/unity- api-1/job/ lp-unity- scope-click- ci/12/rebuild
https:/