From 2a3434a97545598ce6ba8b50e867773d66d5c1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 15 Jan 2024 12:09:08 +0000 Subject: [PATCH 1/8] Update cockpit.js to version 307 --- web/Makefile | 6 +- web/src/lib/cockpit.js | 284 ++++++++++++++--------------------------- 2 files changed, 97 insertions(+), 193 deletions(-) diff --git a/web/Makefile b/web/Makefile index 8e27dc0565..7ac51ba276 100644 --- a/web/Makefile +++ b/web/Makefile @@ -177,15 +177,15 @@ bots: # when you start a new project, use the latest release, and update it from time to time test/common: flock Makefile sh -ec '\ - git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 267; \ + git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 307; \ git checkout --force FETCH_HEAD -- test/common; \ git reset test/common' # checkout Cockpit's PF/React/build library; again this has no API stability guarantee, so check out a stable tag -# TODO: replace the commit with the tag 267 once it is released, which includes cockpit.js as a ES6 module in lib/. +# TODO: replace the commit with the tag 307 once it is released, which includes cockpit.js as a ES6 module in lib/. $(LIB_TEST): flock Makefile sh -ec '\ - git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 267; \ + git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 307; \ git checkout --force FETCH_HEAD -- ../pkg/lib; \ git reset -- ../pkg/lib' mv ../pkg/lib src/ && rmdir ../pkg diff --git a/web/src/lib/cockpit.js b/web/src/lib/cockpit.js index 6704ac5846..34873a73b0 100644 --- a/web/src/lib/cockpit.js +++ b/web/src/lib/cockpit.js @@ -21,10 +21,16 @@ let url_root; -try { - // Sometimes this throws a SecurityError such as during testing - url_root = window.localStorage.getItem('url-root'); -} catch (e) { } +const meta_url_root = document.head.querySelector("meta[name='url-root']"); +if (meta_url_root) { + url_root = meta_url_root.content.replace(/^\/+|\/+$/g, ''); +} else { + // fallback for cockpit-ws < 272 + try { + // Sometimes this throws a SecurityError such as during testing + url_root = window.localStorage.getItem('url-root'); + } catch (e) { } +} /* injected by tests */ var mock = mock || { }; // eslint-disable-line no-use-before-define, no-var @@ -78,7 +84,7 @@ function is_negative(n) { } function invoke_functions(functions, self, args) { - const length = functions ? functions.length : 0; + const length = functions?.length ?? 0; for (let i = 0; i < length; i++) { if (functions[i]) functions[i].apply(self, args); @@ -220,7 +226,7 @@ window.addEventListener('beforeunload', function() { }, false); function transport_debug() { - if (window.debugging == "all" || window.debugging == "channel") + if (window.debugging == "all" || window.debugging?.includes("channel")) console.debug.apply(console, arguments); } @@ -269,7 +275,7 @@ function event_mixin(obj, handlers) { event = new CustomEvent(type, { bubbles: false, cancelable: false, - detail: detail + detail }); args.unshift(event); @@ -288,9 +294,9 @@ function event_mixin(obj, handlers) { function calculate_application() { let path = window.location.pathname || "/"; let _url_root = url_root; - if (window.mock && window.mock.pathname) + if (window.mock?.pathname) path = window.mock.pathname; - if (window.mock && window.mock.url_root) + if (window.mock?.url_root) _url_root = window.mock.url_root; if (_url_root && path.indexOf('/' + _url_root) === 0) @@ -312,9 +318,9 @@ function calculate_url(suffix) { const window_loc = window.location.toString(); let _url_root = url_root; - if (window.mock && window.mock.url) + if (window.mock?.url) return window.mock.url; - if (window.mock && window.mock.url_root) + if (window.mock?.url_root) _url_root = window.mock.url_root; let prefix = calculate_application(); @@ -461,17 +467,6 @@ function Transport() { let check_health_timer; - /* HACK: Compatibility if we're hosted by older Cockpit versions */ - try { - /* See if we should communicate via parent */ - if (!ws && window.parent !== window && window.parent.options && - window.parent.options.protocol == "cockpit1") { - ws = new ParentWebSocket(window.parent); - } - } catch (ex) { - /* permission access errors */ - } - if (!ws) { const ws_loc = calculate_url(); transport_debug("connecting to " + ws_loc); @@ -479,8 +474,6 @@ function Transport() { if (ws_loc) { if ("WebSocket" in window) { ws = new window.WebSocket(ws_loc, "cockpit1"); - } else if ("MozWebSocket" in window) { // Firefox 6 - ws = new window.MozWebSocket(ws_loc); } else { console.error("WebSocket not supported, application will not work!"); } @@ -926,7 +919,7 @@ function Channel(options) { if (!options) options = { }; else if (typeof options == "string") - options = { problem : options }; + options = { problem: options }; options.command = "close"; options.channel = id; @@ -1042,7 +1035,7 @@ function factory() { self.encoding = "utf-8"; self.decode = function decode(data, options) { - const stream = options && options.stream; + const stream = options?.stream; if (data === null || data === undefined) data = ""; @@ -1160,7 +1153,7 @@ function factory() { }, close: function close(problem) { if (default_transport) - default_transport.close(problem ? { problem: problem } : undefined); + default_transport.close(problem ? { problem } : undefined); default_transport = null; this.options = { }; }, @@ -1321,7 +1314,7 @@ function factory() { let then; let done = false; if (is_object(values[0]) || is_function(values[0])) - then = values[0] && values[0].then; + then = values[0]?.then; if (is_function(then)) { state.status = -1; then.call(values[0], function(/* ... */) { @@ -1352,7 +1345,7 @@ function factory() { function deferred_notify(state, values) { const callbacks = state.pending; - if ((state.status <= 0) && callbacks && callbacks.length) { + if ((state.status <= 0) && callbacks?.length) { later_invoke(function() { for (let i = 0, ii = callbacks.length; i < ii; i++) { const result = callbacks[i][0]; @@ -1743,7 +1736,7 @@ function factory() { }; function unclaim() { - if (source && source.close) + if (source?.close) source.close(); source = null; @@ -1986,7 +1979,7 @@ function factory() { } /* Insert our item into the array */ - const entry = { beg: beg, items: items, mapping: mapping }; + const entry = { beg, items, mapping }; if (!head) head = entry; if (tail) @@ -2036,7 +2029,7 @@ function factory() { throw Error("mismatched metric interval between grid and sink"); let gdata = registered[id]; if (!gdata) { - gdata = registered[id] = { grid: grid, links: [] }; + gdata = registered[id] = { grid, links: [] }; gdata.links.remove = function remove() { delete registered[id]; }; @@ -2107,7 +2100,7 @@ function factory() { self.close = function () { for (const id in registered) { const grid = registered[id]; - if (grid && grid.grid) + if (grid?.grid) grid.grid.remove_sink(self); } }; @@ -2182,7 +2175,7 @@ function factory() { const links = sink._register(self, id); if (!links.length) - sinks.push({ sink: sink, links: links }); + sinks.push({ sink, links }); links.push([path, row]); /* Called as add(callback) */ @@ -2452,6 +2445,10 @@ function factory() { const self = this; const application = cockpit.transport.application(); self.url_root = url_root || ""; + + if (window.mock?.url_root) + self.url_root = window.mock.url_root; + if (application.indexOf("cockpit+=") === 0) { if (self.url_root) self.url_root += '/'; @@ -2494,13 +2491,14 @@ function factory() { path = decode_path(path); let href = "/" + path.map(encodeURIComponent).join("/"); - if (with_root && self.url_root && href.indexOf("/" + self.url_root + "/" !== 0)) + if (with_root && self.url_root && href.indexOf("/" + self.url_root + "/") !== 0) href = "/" + self.url_root + href; /* Undo unnecessary encoding of these */ - href = href.replace("%40", "@"); - href = href.replace("%3D", "="); - href = href.replace(/%2B/g, "+"); + href = href.replaceAll("%40", "@"); + href = href.replaceAll("%3D", "="); + href = href.replaceAll("%2B", "+"); + href = href.replaceAll("%23", "#"); let opt; const query = []; @@ -2526,11 +2524,7 @@ function factory() { href = href.substr(1); const pos = href.indexOf('?'); - let first = href; - if (pos === -1) - first = href; - else - first = href.substr(0, pos); + const first = (pos === -1) ? href : href.substr(0, pos); const path = decode_path(first); if (pos !== -1 && options) { href.substring(pos + 1).split("&") @@ -2619,7 +2613,7 @@ function factory() { let hash = window.location.hash; if (hash.indexOf("#") === 0) hash = hash.substring(1); - cockpit.hint("location", { hash: hash }); + cockpit.hint("location", { hash }); cockpit.dispatchEvent("locationchanged"); }); @@ -2630,9 +2624,9 @@ function factory() { cockpit.jump = function jump(path, host) { if (Array.isArray(path)) path = "/" + path.map(encodeURIComponent).join("/") -.replace("%40", "@") -.replace("%3D", "=") -.replace(/%2B/g, "+"); +.replaceAll("%40", "@") +.replaceAll("%3D", "=") +.replaceAll("%2B", "+"); else path = "" + path; @@ -2642,7 +2636,7 @@ function factory() { if (host === undefined) host = cockpit.transport.host; - const options = { command: "jump", location: path, host: host }; + const options = { command: "jump", location: path, host }; cockpit.transport.inject("\n" + JSON.stringify(options)); }; @@ -2729,7 +2723,7 @@ function factory() { } function spawn_debug() { - if (window.debugging == "all" || window.debugging == "spawn") + if (window.debugging == "all" || window.debugging?.includes("spawn")) console.debug.apply(console, arguments); } @@ -2747,6 +2741,8 @@ function factory() { if (options !== undefined) Object.assign(args, options); + spawn_debug("process spawn:", JSON.stringify(args.spawn)); + const name = args.spawn[0] || "process"; const channel = cockpit.channel(args); @@ -2811,7 +2807,7 @@ function factory() { }; function dbus_debug() { - if (window.debugging == "all" || window.debugging == "dbus") + if (window.debugging == "all" || window.debugging?.includes("dbus")) console.debug.apply(console, arguments); } @@ -2926,7 +2922,8 @@ function factory() { iface: { value: iface, enumerable: false, writable: false }, valid: { get: function() { return valid }, enumerable: false }, wait: { - enumerable: false, writable: false, + enumerable: false, + writable: false, value: function(func) { if (func) waits.promise.always(func); @@ -2934,18 +2931,13 @@ function factory() { } }, call: { - value: function(name, args, options) { return client.call(path, iface, name, args, options) }, - enumerable: false, writable: false -}, + value: function(name, args, options) { return client.call(path, iface, name, args, options) }, + enumerable: false, + writable: false + }, data: { value: { }, enumerable: false } }); - if (typeof window.$ === "function") { - Object.defineProperty(self, window.$.expando, { - value: { }, writable: true, enumerable: false - }); - } - if (!options) options = { }; @@ -3024,7 +3016,7 @@ function factory() { } } - client.subscribe({ path: path, interface: iface }, signal, options.subscribe !== false); + client.subscribe({ path, interface: iface }, signal, options.subscribe !== false); function waited(ex) { if (valid) @@ -3035,7 +3027,7 @@ function factory() { /* If watching then do a proper watch, otherwise object is done */ if (options.watch !== false) - client.watch({ path: path, interface: iface }).always(waited); + client.watch({ path, interface: iface }).always(waited); else waited(); } @@ -3051,7 +3043,8 @@ function factory() { iface: { value: iface, enumerable: false, writable: false }, path_namespace: { value: path_namespace, enumerable: false, writable: false }, wait: { - enumerable: false, writable: false, + enumerable: false, + writable: false, value: function(func) { if (func) waits.always(func); @@ -3060,14 +3053,8 @@ function factory() { } }); - if (typeof window.$ === "function") { - Object.defineProperty(self, window.$.expando, { - value: { }, writable: true, enumerable: false - }); - } - /* Subscribe to signals once for all proxies */ - const match = { interface: iface, path_namespace: path_namespace }; + const match = { interface: iface, path_namespace }; /* Callbacks added by proxies */ client.subscribe(match); @@ -3127,7 +3114,6 @@ function factory() { let channel = cockpit.channel(args); const subscribers = { }; - const published = { }; let calls = { }; let cache; @@ -3145,7 +3131,7 @@ function factory() { } function send(payload) { - if (channel && channel.valid) { + if (channel?.valid) { dbus_debug("dbus:", payload); channel.send(payload); return true; @@ -3213,8 +3199,6 @@ function factory() { subscription.callback.apply(self, msg.signal); } } - } else if (msg.call) { - handle(msg.call, msg.id); } else if (msg.notify) { notify(msg.notify); } else if (msg.meta) { @@ -3243,15 +3227,6 @@ function factory() { self.dispatchEvent("meta", data); } - self.meta = function(data, options) { - if (!channel || !channel.valid) - return; - - const message = { ...options, meta: data }; - send(JSON.stringify(message)); - meta(data); - }; - function notify(data) { ensure_cache(); for (const path in data) { @@ -3340,7 +3315,7 @@ function factory() { this.subscribe = function subscribe(match, callback, rule) { const subscription = { match: { ...match }, - callback: callback + callback }; if (rule !== false) @@ -3374,7 +3349,7 @@ function factory() { last_cookie++; const dfd = cockpit.defer(); - const msg = JSON.stringify({ watch: match, id: id }); + const msg = JSON.stringify({ watch: match, id }); if (send(msg)) calls[id] = dfd; else @@ -3391,91 +3366,12 @@ function factory() { return ret; }; - function unknown_interface(path, iface) { - const message = "DBus interface " + iface + " not available at " + path; - return cockpit.reject(new DBusError(["org.freedesktop.DBus.Error.UnknownInterface", [message]])); - } - - function unknown_method(path, iface, method) { - const message = "DBus method " + iface + " " + method + " not available at " + path; - return cockpit.reject(new DBusError(["org.freedesktop.DBus.Error.UnknownMethod", [message]])); - } - - function not_implemented(path, iface, method) { - console.warn("method is not implemented properly: ", path, iface, method); - return unknown_method(path, iface, method); - } - - function invoke(call) { - const path = call[0]; - const iface = call[1]; - const method = call[2]; - const object = published[path + "\n" + iface]; - const info = cache.meta[iface]; - if (!object || !info) - return unknown_interface(path, iface); - if (!info.methods || !(method in info.methods)) - return unknown_method(path, iface, method); - if (typeof object[method] != "function") - return not_implemented(path, iface, method); - return object[method].apply(object, call[3]); - } - - function handle(call, cookie) { - const result = invoke(call); - if (!cookie) - return; /* Discard result */ - cockpit.when(result).then(function() { - let out = Array.prototype.slice.call(arguments, 0); - if (out.length == 1 && typeof out[0] == "undefined") - out = []; - send(JSON.stringify({ reply: [out], id: cookie })); - }, function(ex) { - const error = []; - error[0] = ex.name || " org.freedesktop.DBus.Error.Failed"; - error[1] = [cockpit.message(ex) || error[0]]; - send(JSON.stringify({ error: error, id: cookie })); - }); - } - - self.publish = function(path, iface, object, options) { - const publish = [path, iface]; - - const id = String(last_cookie); - last_cookie++; - const dfd = calls[id] = cockpit.defer(); - - const payload = JSON.stringify({ ...options, publish, id }); - - if (send(payload)) - calls[id] = dfd; - else - dfd.reject(new DBusError(closed)); - - const key = path + "\n" + iface; - dfd.promise.then(function() { - published[key] = object; - }); - - /* Return a way to remove this object */ - const ret = dfd.promise; - ret.remove = function remove() { - if (id in calls) { - dfd.reject(new DBusError("cancelled")); - delete calls[id]; - } - delete published[key]; - send(JSON.stringify({ unpublish: publish })); - }; - return ret; - }; - self.proxy = function proxy(iface, path, options) { if (!iface) iface = name; iface = String(iface); if (!path) - path = "/" + iface.replace(/\./g, "/"); + path = "/" + iface.replaceAll(".", "/"); let Constructor = self.constructors[iface]; if (!Constructor) Constructor = self.constructors["*"]; @@ -3561,28 +3457,28 @@ function factory() { const binary = options.binary; const self = { - path: path, - read: read, - replace: replace, - modify: modify, + path, + read, + replace, + modify, - watch: watch, + watch, - close: close + close }; const base_channel_options = { ...options }; delete base_channel_options.syntax; function parse(str) { - if (options.syntax && options.syntax.parse) + if (options.syntax?.parse) return options.syntax.parse(str); else return str; } function stringify(obj) { - if (options.syntax && options.syntax.stringify) + if (options.syntax?.stringify) return options.syntax.stringify(obj); else return obj; @@ -3599,7 +3495,7 @@ function factory() { const opts = { ...base_channel_options, payload: "fsread1", - path: path + path }; function try_read() { @@ -3668,7 +3564,7 @@ function factory() { const opts = { ...base_channel_options, payload: "fsreplace1", - path: path, + path, tag: expected_tag }; replace_channel = cockpit.channel(opts); @@ -3739,7 +3635,7 @@ function factory() { const opts = { payload: "fswatch1", - path: path, + path, superuser: base_channel_options.superuser, }; watch_channel = cockpit.channel(opts); @@ -3812,9 +3708,11 @@ function factory() { let po_plural; cockpit.language = "en"; + cockpit.language_direction = "ltr"; cockpit.locale = function locale(po) { let lang = cockpit.language; + let lang_dir = cockpit.language_direction; let header; if (po) { @@ -3829,9 +3727,12 @@ function factory() { po_plural = header["plural-forms"]; if (header.language) lang = header.language; + if (header["language-direction"]) + lang_dir = header["language-direction"]; } cockpit.language = lang; + cockpit.language_direction = lang_dir; }; cockpit.translate = function translate(/* ... */) { @@ -3894,7 +3795,7 @@ function factory() { const key = context ? context + '\u0004' + string : string; if (po_data) { const translated = po_data[key]; - if (translated && translated[1]) + if (translated?.[1]) return translated[1]; } return string; @@ -3994,7 +3895,7 @@ function factory() { } function http_debug() { - if (window.debugging == "all" || window.debugging == "http") + if (window.debugging == "all" || window.debugging?.includes("http")) console.debug.apply(console, arguments); } @@ -4186,10 +4087,10 @@ function factory() { self.get = function get(path, params, headers) { return self.request({ method: "GET", - params: params, - path: path, + params, + path, body: "", - headers: headers + headers }); }; @@ -4208,9 +4109,9 @@ function factory() { return self.request({ method: "POST", - path: path, - body: body, - headers: headers + path, + body, + headers }); }; @@ -4268,7 +4169,7 @@ function factory() { if (options) group = options.group; - if (options && options.admin) + if (options?.admin) admin = true; function decide(user) { @@ -4350,7 +4251,7 @@ function factory() { const options = { payload: "metrics1", - interval: interval, + interval, source: "internal", ...options_list[0] }; @@ -4450,7 +4351,7 @@ function factory() { mapping = { }; meta.metrics.forEach(function(metric, i) { const map = { "": i }; - const name = options.metrics_path_names ? options.metrics_path_names[i] : metric.name; + const name = options.metrics_path_names?.[i] ?? metric.name; mapping[name] = map; if (metric.instances) { metric.instances.forEach(function(instance, i) { @@ -4477,8 +4378,8 @@ function factory() { archive_options_list.push({ ...options_list[i], source: options_list[i].archive_source, - timestamp: timestamp, - limit: limit + timestamp, + limit }); } } @@ -4519,7 +4420,10 @@ function factory() { const old_onerror = window.onerror; window.onerror = function(msg, url, line) { - cockpit.oops(); + // Errors with url == "" are not logged apparently, so let's + // not show the "Oops" for them either. + if (url != "") + cockpit.oops(); if (old_onerror) return old_onerror(msg, url, line); return false; From bb10d86661fb817c28884799f6c634c940028148 Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Fri, 9 Feb 2024 22:44:48 +0100 Subject: [PATCH 2/8] improve detection of multipath config --- service/lib/agama/storage/manager.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/service/lib/agama/storage/manager.rb b/service/lib/agama/storage/manager.rb index 21d7543efe..d8738fa142 100644 --- a/service/lib/agama/storage/manager.rb +++ b/service/lib/agama/storage/manager.rb @@ -293,13 +293,8 @@ def check_multipath mods = `lsmod`.lines.grep(/dm_multipath/) logger.warn("dm_multipath modules is not loaded") if mods.empty? - conf_file = File.exist?(MULTIPATH_CONFIG) - if conf_file - finder = File.readlines(MULTIPATH_CONFIG).grep(/find_multipaths\s+smart/) - logger.warn("find_multipaths is not set to smart value") if finder.empty? - else - logger.warn("#{MULTIPATH_CONFIG} does not exist") - end + conf = `multipath -t`.lines.grep(/find_multipaths "smart"/) + logger.warn("multipath: find_multipaths is not set to 'smart'") if conf.empty? end end end From c003566b607fbc4d85bfabfdc099f291340d3137 Mon Sep 17 00:00:00 2001 From: YaST Bot Date: Sun, 11 Feb 2024 02:44:09 +0000 Subject: [PATCH 3/8] Update web PO files Agama-weblate commit: df45d6338b889abe2caa0c8977e2176d6b4ee175 --- web/po/cs.po | 44 +++++++-------- web/po/de.po | 44 +++++++-------- web/po/es.po | 126 ++++++++++++++++++++---------------------- web/po/fr.po | 50 ++++++++--------- web/po/id.po | 50 ++++++++--------- web/po/ja.po | 50 ++++++++--------- web/po/mk.po | 44 +++++++-------- web/po/nl.po | 50 ++++++++--------- web/po/pt_BR.po | 50 ++++++++--------- web/po/ru.po | 50 ++++++++--------- web/po/sv.po | 50 ++++++++--------- web/po/uk.po | 44 +++++++-------- web/src/manifest.json | 8 +-- 13 files changed, 318 insertions(+), 342 deletions(-) diff --git a/web/po/cs.po b/web/po/cs.po index a6daa2b978..719771ea29 100644 --- a/web/po/cs.po +++ b/web/po/cs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-28 02:07+0000\n" +"POT-Creation-Date: 2024-02-07 02:04+0000\n" "PO-Revision-Date: 2023-12-31 20:39+0000\n" "Last-Translator: Ladislav Slezák \n" "Language-Team: Czech \n" "Language-Team: German \n" "Language-Team: Spanish \n" @@ -92,7 +92,7 @@ msgstr "Recargar" #: src/components/core/DevelopmentInfo.jsx:55 msgid "Cockpit server" -msgstr "" +msgstr "Servidor Cockpit" #: src/components/core/FileViewer.jsx:65 msgid "Reading file..." @@ -103,13 +103,12 @@ msgid "Cannot read the file" msgstr "No se puede leer el archivo" #: src/components/core/InstallButton.jsx:37 -#, fuzzy msgid "" "There are some reported issues. Please review them in the previous steps " "before proceeding with the installation." msgstr "" -"Hay algunos problemas reportados. Por favor, consulte [la lista de " -"problemas] antes de continuar con la instalación." +"Hay algunos problemas reportados. Por favor, revíselos en los pasos " +"anteriores antes de continuar con la instalación." #: src/components/core/InstallButton.jsx:49 msgid "Confirm Installation" @@ -173,22 +172,23 @@ msgstr "Instalar" #: src/components/core/InstallationFinished.jsx:41 msgid "TPM sealing requires the new system to be booted directly." -msgstr "" +msgstr "El sellado TPM requiere que el nuevo sistema se inicie directamente." #: src/components/core/InstallationFinished.jsx:46 msgid "" "If a local media was used to run this installer, remove it before the next " "boot." msgstr "" +"Si se utilizó un medio local para ejecutar este instalador, expúlselo antes " +"del próximo inicio." #: src/components/core/InstallationFinished.jsx:50 -#, fuzzy msgid "Hide details" -msgstr "Detalles" +msgstr "Ocultar detalles" #: src/components/core/InstallationFinished.jsx:50 msgid "See more details" -msgstr "" +msgstr "Ver más detalles" #. TRANSLATORS: Do not translate 'abbr' and 'title', they are part of the HTML markup #: src/components/core/InstallationFinished.jsx:55 @@ -198,6 +198,10 @@ msgid "" "first boot of the new system. For that to work, the machine needs to boot " "directly to the new boot loader." msgstr "" +"El último paso para configurar el TPM para abrir automáticamente dispositivos cifrados se llevará a cabo " +"durante el primer inicio del nuevo sistema. Para que eso funcione, la " +"máquina debe iniciarse directamente en el nuevo gestor de arranque." #. TRANSLATORS: page title #: src/components/core/InstallationFinished.jsx:88 @@ -270,9 +274,8 @@ msgid "Show global options" msgstr "Mostrar opciones globales" #: src/components/core/Page.jsx:215 -#, fuzzy msgid "Page Actions" -msgstr "Acciones planeadas" +msgstr "Acciones de página" #: src/components/core/PasswordAndConfirmationInput.jsx:35 msgid "Passwords do not match" @@ -338,9 +341,8 @@ msgid "Installer Options" msgstr "Opciones del instalador" #: src/components/core/Sidebar.jsx:128 -#, fuzzy msgid "Hide installer options" -msgstr "Opciones del instalador" +msgstr "Ocultar las opciones del instalador" #: src/components/core/Sidebar.jsx:136 msgid "Diagnostic tools" @@ -348,24 +350,20 @@ msgstr "Herramientas de diagnóstico" #. TRANSLATORS: Titles used for the popup displaying found section issues #: src/components/core/ValidationErrors.jsx:53 -#, fuzzy msgid "Software issues" -msgstr "Software %s" +msgstr "Problemas de software" #: src/components/core/ValidationErrors.jsx:54 -#, fuzzy msgid "Product issues" -msgstr "Producto" +msgstr "Problemas del producto" #: src/components/core/ValidationErrors.jsx:55 -#, fuzzy msgid "Storage issues" -msgstr "Mostrar los problemas" +msgstr "Problemas de almacenamiento" #: src/components/core/ValidationErrors.jsx:57 -#, fuzzy msgid "Found Issues" -msgstr "Mostrar los problemas" +msgstr "Problemas encontrados" #. TRANSLATORS: %d is replaced with the number of errors found #: src/components/core/ValidationErrors.jsx:77 @@ -403,8 +401,8 @@ msgstr "" "El idioma utilizado por el instalador. El idioma del sistema instalado se " "puede configurar en la página %s." -#. TRANSLATORS: page title #. TRANSLATORS: page section +#. TRANSLATORS: page title #: src/components/l10n/InstallerLocaleSwitcher.jsx:56 #: src/components/l10n/L10nPage.jsx:384 #: src/components/overview/L10nSection.jsx:52 @@ -619,38 +617,39 @@ msgstr "Prefijo IP o máscara de red" msgid "At least one address must be provided for selected mode" msgstr "Se debe proporcionar al menos una dirección para el modo seleccionado" -#: src/components/network/IpSettingsForm.jsx:131 +#. TRANSLATORS: %s is replaced by the iSCSI target node name +#: src/components/network/IpSettingsForm.jsx:133 +#: src/components/storage/iscsi/EditNodeForm.jsx:50 #, c-format -msgid "Edit %s connection" -msgstr "Editar %s conexión" +msgid "Edit %s" +msgstr "Editar %s" #. TRANSLATORS: network connection mode (automatic via DHCP or manual with static IP) -#: src/components/network/IpSettingsForm.jsx:133 -#: src/components/network/IpSettingsForm.jsx:138 -#: src/components/network/IpSettingsForm.jsx:140 +#: src/components/network/IpSettingsForm.jsx:136 +#: src/components/network/IpSettingsForm.jsx:141 +#: src/components/network/IpSettingsForm.jsx:143 msgid "Mode" msgstr "Modo" -#: src/components/network/IpSettingsForm.jsx:144 +#: src/components/network/IpSettingsForm.jsx:147 msgid "Automatic (DHCP)" msgstr "Automático (DHCP)" #. TRANSLATORS: manual network configuration mode with a static IP address -#: src/components/network/IpSettingsForm.jsx:146 +#: src/components/network/IpSettingsForm.jsx:149 #: src/components/storage/iscsi/NodeStartupOptions.js:25 msgid "Manual" msgstr "Manual" #. TRANSLATORS: network gateway configuration -#: src/components/network/IpSettingsForm.jsx:161 #: src/components/network/IpSettingsForm.jsx:164 +#: src/components/network/IpSettingsForm.jsx:167 msgid "Gateway" msgstr "Puerta de enlace" #: src/components/network/NetworkPage.jsx:38 -#, fuzzy msgid "No wired connections found." -msgstr "No se encontraron conexiones por cable" +msgstr "No se encontraron conexiones por cable." #: src/components/network/NetworkPage.jsx:53 msgid "" @@ -666,9 +665,8 @@ msgstr "" "hardware o está deshabilitado." #: src/components/network/NetworkPage.jsx:58 -#, fuzzy msgid "No WiFi connections found." -msgstr "No se encontraron conexiones WiFi" +msgstr "No se encontraron conexiones WiFi." #. TRANSLATORS: button label #: src/components/network/NetworkPage.jsx:70 @@ -679,18 +677,18 @@ msgstr "Conectado a una red WIFI" #. TRANSLATORS: page section title #. TRANSLATORS: page title -#: src/components/network/NetworkPage.jsx:170 -#: src/components/overview/NetworkSection.jsx:102 +#: src/components/network/NetworkPage.jsx:169 +#: src/components/overview/NetworkSection.jsx:83 msgid "Network" msgstr "Red" #. TRANSLATORS: page section -#: src/components/network/NetworkPage.jsx:172 +#: src/components/network/NetworkPage.jsx:171 msgid "Wired networks" msgstr "Redes cableadas" #. TRANSLATORS: page section -#: src/components/network/NetworkPage.jsx:177 +#: src/components/network/NetworkPage.jsx:176 msgid "WiFi networks" msgstr "Redes WIFI" @@ -788,13 +786,13 @@ msgstr "Olvidar red" msgid "The system will use %s as its default language." msgstr "El sistema utilizará %s como su idioma predeterminado." -#: src/components/overview/NetworkSection.jsx:78 +#: src/components/overview/NetworkSection.jsx:59 msgid "No network connections detected" msgstr "No se detectaron conexiones de red" #. TRANSLATORS: header for the list of active network connections, #. %d is replaced by the number of active connections -#: src/components/overview/NetworkSection.jsx:87 +#: src/components/overview/NetworkSection.jsx:68 #, c-format msgid "%d connection set:" msgid_plural "%d connections set:" @@ -812,8 +810,8 @@ msgstr "Resumen de la instalación" msgid "%s (registered)" msgstr "%s (registrado)" -#. TRANSLATORS: page section #. TRANSLATORS: page title +#. TRANSLATORS: page section #: src/components/overview/ProductSection.jsx:71 #: src/components/product/ProductPage.jsx:435 msgid "Product" @@ -828,8 +826,8 @@ msgstr "Leer repositorios de software" msgid "Refresh the repositories" msgstr "Refrescar los repositorios" -#. TRANSLATORS: page title #. TRANSLATORS: page section +#. TRANSLATORS: page title #: src/components/overview/SoftwareSection.jsx:143 #: src/components/software/SoftwarePage.jsx:81 msgid "Software" @@ -1232,9 +1230,8 @@ msgid "Installation device" msgstr "Dispositivo de instalación" #: src/components/storage/ProposalSettingsSection.jsx:154 -#, fuzzy msgid "No devices found." -msgstr "No se encontraron dispositivos" +msgstr "No se encontraron dispositivos." #: src/components/storage/ProposalSettingsSection.jsx:241 msgid "Devices for creating the volume group" @@ -1279,15 +1276,18 @@ msgid "" "system. TPM sealing requires the new system to be booted directly on its " "first run." msgstr "" +"La contraseña no será necesaria para iniciar y acceder a los datos si el " +"TPM puede verificar la " +"integridad del sistema. El sellado TPM requiere que el nuevo sistema se " +"inicie directamente en su primera ejecución." #: src/components/storage/ProposalSettingsSection.jsx:440 msgid "Use the TPM to decrypt automatically on each boot" -msgstr "" +msgstr "Utilice el TPM para descifrar automáticamente en cada arranque" #: src/components/storage/ProposalSettingsSection.jsx:511 -#, fuzzy msgid "Change encryption settings" -msgstr "Configuraciones del cifrado" +msgstr "Cambiar las configuraciones de cifrado" #: src/components/storage/ProposalSettingsSection.jsx:516 #: src/components/storage/ProposalSettingsSection.jsx:537 @@ -1418,9 +1418,8 @@ msgid "File systems to create in your system" msgstr "Sistemas de archivos para crear en su sistema" #: src/components/storage/VolumeForm.jsx:102 -#, fuzzy msgid "Select a value" -msgstr "Seleccionar el idioma" +msgstr "Seleccionar un valor" #. TRANSLATORS: description of Btrfs snapshots feature. #: src/components/storage/VolumeForm.jsx:212 @@ -1590,9 +1589,8 @@ msgid "Deactivated" msgstr "Desactivado" #: src/components/storage/ZFCPPage.jsx:417 -#, fuzzy msgid "No zFCP controllers found." -msgstr "No se encontraron controladores zFCP" +msgstr "No se encontraron controladores zFCP." #: src/components/storage/ZFCPPage.jsx:418 msgid "Please, try to read the zFCP devices again." @@ -1640,9 +1638,8 @@ msgid "Activate zFCP disk" msgstr "Activar disco zFCP" #: src/components/storage/ZFCPPage.jsx:553 -#, fuzzy msgid "No zFCP disks found." -msgstr "No se encontraron discos zFCP" +msgstr "No se encontraron discos zFCP." #. TRANSLATORS: button label #: src/components/storage/ZFCPPage.jsx:572 @@ -1791,12 +1788,6 @@ msgstr "Puerto" msgid "Incorrect port" msgstr "Puerto incorrecto" -#. TRANSLATORS: %s is replaced by the iSCSI target node name -#: src/components/storage/iscsi/EditNodeForm.jsx:50 -#, c-format -msgid "Edit %s" -msgstr "Editar %s" - #: src/components/storage/iscsi/InitiatorForm.jsx:42 msgid "Edit iSCSI Initiator" msgstr "Editar iniciador iSCSI" @@ -1870,9 +1861,8 @@ msgid "Interface" msgstr "Interfaz" #: src/components/storage/iscsi/TargetsSection.jsx:139 -#, fuzzy msgid "No iSCSI targets found." -msgstr "No se encontraron objetivos iSCSI" +msgstr "No se encontraron objetivos iSCSI." #: src/components/storage/iscsi/TargetsSection.jsx:140 msgid "" @@ -1985,9 +1975,8 @@ msgid "PiB" msgstr "PB" #: src/components/users/FirstUser.jsx:44 -#, fuzzy msgid "No user defined yet." -msgstr "Ningún usuario definido todavía" +msgstr "Ningún usuario definido todavía." #: src/components/users/FirstUser.jsx:47 msgid "" @@ -2047,9 +2036,8 @@ msgid "Auto-login" msgstr "Inicio de sesión automático" #: src/components/users/RootAuthMethods.jsx:35 -#, fuzzy msgid "No root authentication method defined yet." -msgstr "Aún no se ha definido ningún método de autenticación de root" +msgstr "Aún no se ha definido ningún método de autenticación de root." #: src/components/users/RootAuthMethods.jsx:38 msgid "" @@ -2147,6 +2135,10 @@ msgstr "Usuario" msgid "Root authentication" msgstr "Autenticación de root" +#, c-format +#~ msgid "Edit %s connection" +#~ msgstr "Editar %s conexión" + #~ msgid "Have a lot of fun! Your openSUSE Development Team." #~ msgstr "Have a lot of fun! El equipo de desarrollo de openSUSE." diff --git a/web/po/fr.po b/web/po/fr.po index c4f81a397f..e82423fefe 100644 --- a/web/po/fr.po +++ b/web/po/fr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-28 02:07+0000\n" +"POT-Creation-Date: 2024-02-07 02:04+0000\n" "PO-Revision-Date: 2024-01-23 12:59+0000\n" "Last-Translator: faila fail \n" "Language-Team: French \n" "Language-Team: Indonesian \n" "Language-Team: Japanese \n" "Language-Team: Macedonian \n" "Language-Team: Dutch \n" "Language-Team: Portuguese (Brazil) \n" "Language-Team: Russian \n" "Language-Team: Swedish \n" "Language-Team: Ukrainian Date: Sun, 11 Feb 2024 02:51:45 +0000 Subject: [PATCH 4/8] Update translations in the product files Agama-weblate commit: df45d6338b889abe2caa0c8977e2176d6b4ee175 --- products.d/microos-desktop.yaml | 4 ++++ products.d/microos.yaml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/products.d/microos-desktop.yaml b/products.d/microos-desktop.yaml index 966b65415f..97d351d864 100644 --- a/products.d/microos-desktop.yaml +++ b/products.d/microos-desktop.yaml @@ -12,6 +12,10 @@ description: 'A distribution for the desktop offering automatic updates and # Do not manually change any translations! See README.md for more details. translations: description: + es: Una distribución para escritorio que ofrece actualizaciones automáticas y + reversión sobre los fundamentos de openSUSE MicroOS. Incluye Podman + Container Runtime y permite administrar software usando Gnome Software o + KDE Discover. fr: Une distribution pour le bureau offrant des mises à jour automatiques et un rétablissement au dessus des fondations d'openSUSE MicroOS. Inclut Podman Container Runtime et permet de gérer les logiciels en utilisant Gnome diff --git a/products.d/microos.yaml b/products.d/microos.yaml index 6ddd801f50..a0cec5d775 100644 --- a/products.d/microos.yaml +++ b/products.d/microos.yaml @@ -12,6 +12,11 @@ description: 'A quick, small distribution designed to host container workloads # Do not manually change any translations! See README.md for more details. translations: description: + es: Una distribución pequeña y rápida diseñada para alojar cargas de trabajo de + contenedores con administración y parches automatizados. openSUSE MicroOS + proporciona actualizaciones transaccionales (atómicas) en un sistema de + archivos raíz btrfs de solo lectura. Como distribución de lanzamiento + continuo, el software siempre está actualizado. fr: Une petite distribution rapide conçue pour héberger des charges de travail de conteneurs avec une administration et des correctifs automatisés. openSUSE MicroOS fournit des mises à jour transactionnelles (atomiques) From 20c122622b5dfaaf298c7664d66369548ccb4aae Mon Sep 17 00:00:00 2001 From: Josef Reidinger Date: Mon, 12 Feb 2024 10:47:30 +0100 Subject: [PATCH 5/8] add detection if binary is there --- service/lib/agama/storage/manager.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/service/lib/agama/storage/manager.rb b/service/lib/agama/storage/manager.rb index d8738fa142..10ba71ed69 100644 --- a/service/lib/agama/storage/manager.rb +++ b/service/lib/agama/storage/manager.rb @@ -293,8 +293,13 @@ def check_multipath mods = `lsmod`.lines.grep(/dm_multipath/) logger.warn("dm_multipath modules is not loaded") if mods.empty? - conf = `multipath -t`.lines.grep(/find_multipaths "smart"/) - logger.warn("multipath: find_multipaths is not set to 'smart'") if conf.empty? + binary = system("which multipath") + if binary + conf = `multipath -t`.lines.grep(/find_multipaths "smart"/) + logger.warn("multipath: find_multipaths is not set to 'smart'") if conf.empty? + else + logger.warn("multipath is not installed.") + end end end end From d3ce7b67a59b12dd9468e098ec0f1f31542f9423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 12 Feb 2024 10:50:57 +0000 Subject: [PATCH 6/8] [doc] Document the new Agama architecture (#1033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a document explaining the proposal for the new Agama architecture. Let's consider the document as a starting point for further discussion. You can see the [rendered version](https://github.com/openSUSE/agama/blob/new-architecture/doc/new_architecture.md) if you prefer. PS: please, forgive me for those diagrams 😅 --- doc/new_architecture.md | 173 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 doc/new_architecture.md diff --git a/doc/new_architecture.md b/doc/new_architecture.md new file mode 100644 index 0000000000..a0d9b087e6 --- /dev/null +++ b/doc/new_architecture.md @@ -0,0 +1,173 @@ +# Agama's 2024 architecture + +This document describes the proposal for the new Agama architecture. The reasons for introducing +these changes are recorded in [a discussion in Agama's repository][drop-cockpit]. + +[drop-cockpit]: https://github.com/openSUSE/agama/discussions/1000 + +But before describing how the architecture should look, let's quickly look at the current status. + +## The current architecture + +At this point, Agama is composed of four high-level components: + +* **Agama service**: implements the logic to perform the installation. It is the core component of +Agama and it offers a D-Bus interface. Actually, it is composed of two services: `rubygem-agama` and +`agama-dbus-server`. + +* **Web user interface (`cockpit-agama`)**: a web-based interface that plays the role of a GUI when +using Agama live. + +* **Command Line Interface (`agama-cli`)**: it allows to interact with Agama core and drives the +auto-installation process. + +* **Auto-installation (`autoinstallation`)**: it is composed by a Systemd service (`agama-auto`) and +a script that relies on `agama-cli`. + +In addition to those components, we need to consider Cockpit, which plays a vital role: + +* It makes communication between the web UI and the D-Bus services possible. +* It makes the web UI code available to the browser. +* It takes care of authenticating the user when connecting remotely. Again, it is only relative to +the web UI. + +```mermaid +flowchart LR + subgraph Clients + Browser + CLI + auto + end + + subgraph Cockpit + UI["Web UI"] + end + + subgraph Agama Service + Rust[Agama Rust] + Ruby[Agama Ruby] + end + + Browser <---> UI + UI <--D-Bus--> Rust + UI <--D-Bus--> Ruby + CLI <--D-Bus--> Rust + CLI <--D-Bus--> Ruby + Rust <--D-Bus---> Ruby + auto --> CLI +``` + +## The new architecture + +The proposed architecture is not that different from the current one, but it tries to meet these +goals: + +* Drop our dependency on Cockpit. +* Implement a higher-level API to be consumed by the clients, replacing D-Bus for client-server +communication. Agama will still use D-Bus for IPC between the Rust and Ruby components. + +### Components + +With those goals in mind, we are considering the following components: + +* **Agama core (old `agama-dbus-server`)**: implements the installation logic. It relies heavily on +the Agama YaST service. + +* **Agama YaST service (old `rubygem-agama`)**: it is written in Ruby and has direct access to YaST +libraries. Complex parts, like storage and software handling, are implemented in this component. + +* **HTTP and WebSocket API**: implements the API the clients should use to communicate with Agama. +Under the hood, it still uses D-Bus for communication between Agama core and Agama YaST. + +* **Web user interface (old `cockpit-agama`)**: Agama's graphical user interface. The web server +makes this React application available to the browsers. + +* **Command Line Interface (`agama-cli`)**: it allows interaction with Agama and drives the +auto-installation process. With the new architecture, connecting through the network might be +possible without SSH. + +* **Auto-installation (`autoinstallation`)**: as in the current architecture, it is composed by +a Systemd service (`agama-auto`) and a script that relies on `agama-cli`. + +The following diagram could be better, but it represents the main components and their interactions. + +```mermaid +flowchart LR + subgraph Clients + Browser + CLI + auto + end + + subgraph Agama Core + subgraph Web["Web Server"] + direction TB + UI["Web UI"] + API["HTTP/WS API"] + WS["WebSocket"] + end + + Web <--"Channel" --> Rust + end + Rust <-- "D-Bus" --> YaST["Agama YaST"] + + Browser --> UI + Browser --> API + Browser <---> WS + CLI --> API + CLI <---> WS + auto --> CLI +``` + +### The web-based API + +The new web-based API is divided into two different parts: + +* **HTTP/JSON API**: allows clients to execute actions or query Agama. For instance, it could get +the list of available products, request a new storage proposal or start the installation. About the +approach, something REST-like is the most suitable. Switching to GraphQL or gRPC do not seem to be +needed in our case (todo: write why). + +* **WebSocket**: Agama will use WebSockets to notify clients about any relevant event: progress, +configuration changes, etc. The event's format is not defined yet, but a JSON event containing the +`type` and the `payload/details` should be enough[^topics]. + +[^topics]: Ideally, the clients should be able to subscribe to the topics they are interested in. + But that feature can wait. + +### Encryption + +In the case of a remote installation, the communication between the clients and the server must be +encrypted. Connecting to port 80 (HTTP) should redirect the client to port 443 (HTTPS). + +About the certificate, Agama will use a self-signed certificate unless the user injects its own +certificate (through a kernel command line option). + +NOTE: under discussion. + +### Authentication + +The HTTP interface should allow authentication specifying a user and password that will be checked +against PAM. It is not clear yet, but we might need to check whether the logged user has permissions +(making sure it is `root` or through Polkit). + +On successful authentication, the server generates a [JSON Web Token][jwt] that the client will +include in the subsequent requests. The web client stores the token in an HTTP-only +cookie[^http-only] and the CLI uses a file with restricted permissions. + +[^http-only] HTTP-only cookies cannot be accessed byt client-side JavaScript. + +#### Skipping the authentication + +When using Agama locally in the installation media, it would be unpleasant to ask for a +user/password. For that reason, there must be a mechanism to skip the authentication step. Agama +Live could run a special service that generates a valid token and injects such a token into the +server, the CLI and the web browser. + +## Links + +* https://bugzilla.suse.com/show_bug.cgi?id=1219688 +* https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html + +[http-auth]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication +[jwt]: https://jwt.io From 9680d0e855a2da23927c93548bd7746f34459eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 12 Feb 2024 11:53:07 +0000 Subject: [PATCH 7/8] Update to Cockpit 309 --- web/Makefile | 6 +++--- web/src/lib/cockpit.js | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/web/Makefile b/web/Makefile index 7ac51ba276..07b3cb7f84 100644 --- a/web/Makefile +++ b/web/Makefile @@ -177,15 +177,15 @@ bots: # when you start a new project, use the latest release, and update it from time to time test/common: flock Makefile sh -ec '\ - git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 307; \ + git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 309; \ git checkout --force FETCH_HEAD -- test/common; \ git reset test/common' # checkout Cockpit's PF/React/build library; again this has no API stability guarantee, so check out a stable tag -# TODO: replace the commit with the tag 307 once it is released, which includes cockpit.js as a ES6 module in lib/. +# TODO: replace the commit with the tag 309 once it is released, which includes cockpit.js as a ES6 module in lib/. $(LIB_TEST): flock Makefile sh -ec '\ - git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 307; \ + git fetch --depth=1 https://github.com/cockpit-project/cockpit.git 309; \ git checkout --force FETCH_HEAD -- ../pkg/lib; \ git reset -- ../pkg/lib' mv ../pkg/lib src/ && rmdir ../pkg diff --git a/web/src/lib/cockpit.js b/web/src/lib/cockpit.js index 34873a73b0..bef8e61033 100644 --- a/web/src/lib/cockpit.js +++ b/web/src/lib/cockpit.js @@ -3709,6 +3709,7 @@ function factory() { cockpit.language = "en"; cockpit.language_direction = "ltr"; + const test_l10n = window.localStorage.test_l10n; cockpit.locale = function locale(po) { let lang = cockpit.language; @@ -3796,8 +3797,12 @@ function factory() { if (po_data) { const translated = po_data[key]; if (translated?.[1]) - return translated[1]; + string = translated[1]; } + + if (test_l10n === 'true') + return "»" + string + "«"; + return string; }; From 06aa47c1a34bcba302ac706265f07beb20154f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 12 Feb 2024 11:54:54 +0000 Subject: [PATCH 8/8] Update changes file --- web/package/cockpit-agama.changes | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/package/cockpit-agama.changes b/web/package/cockpit-agama.changes index 11aa800032..6769afa843 100644 --- a/web/package/cockpit-agama.changes +++ b/web/package/cockpit-agama.changes @@ -1,7 +1,12 @@ +------------------------------------------------------------------- +Mon Feb 12 11:53:29 UTC 2024 - Imobach Gonzalez Sosa + +- Update cockpit.js to version 309 (gh#openSUSE/agama#1038). + ------------------------------------------------------------------- Mon Jan 29 14:34:37 UTC 2024 - Knut Anderssen -- Partly replacing the NetworkManager client by the agama one +- Partly replacing the NetworkManager client by the Agama one (gh#openSUSE/agama#1006). ------------------------------------------------------------------- @@ -9,7 +14,7 @@ Fri Jan 19 09:34:26 UTC 2024 - Nagu - Storage UI: show mount point selector only when the user can change it. (gh#openSUSE/agama#1007) - + ------------------------------------------------------------------- Thu Jan 18 08:33:52 UTC 2024 - Ancor Gonzalez Sosa