From 6abef5d0516f0e376ddedb62d552b15052e141d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Tue, 3 Dec 2024 22:15:14 +0000
Subject: [PATCH 001/147] feat(products): add a SLES 15.6 definition
* Only for testing purposes.
---
products.d/agama-products.spec | 1 +
products.d/sles_156.yaml | 162 +++++++++++++++++++++++++++++++++
2 files changed, 163 insertions(+)
create mode 100644 products.d/sles_156.yaml
diff --git a/products.d/agama-products.spec b/products.d/agama-products.spec
index 10e6448cc2..4db27c60a9 100644
--- a/products.d/agama-products.spec
+++ b/products.d/agama-products.spec
@@ -65,6 +65,7 @@ Definition of SLE-based products (e.g., SUSE Linux Enterprise Server) for the Ag
%license LICENSE
%dir %{_datadir}/agama
%dir %{_datadir}/agama/products.d
+%{_datadir}/agama/products.d/sles_156.yaml
%{_datadir}/agama/products.d/sles_160.yaml
%{_datadir}/agama/products.d/sles_sap_160.yaml
diff --git a/products.d/sles_156.yaml b/products.d/sles_156.yaml
new file mode 100644
index 0000000000..cf242e68a0
--- /dev/null
+++ b/products.d/sles_156.yaml
@@ -0,0 +1,162 @@
+id: SLES
+name: SUSE Linux Enterprise Server 15.6
+# ------------------------------------------------------------------------------
+# WARNING: When changing the product description delete the translations located
+# at the at translations/description key below to avoid using obsolete
+# translations!!
+# ------------------------------------------------------------------------------
+description: "An open, reliable, compliant, and future-proof Linux Server choice
+ that ensures the enterprise's business continuity. It is the secure and
+ adaptable OS for long-term supported, innovation-ready infrastructure running
+ business-critical workloads on-premises, in the cloud, and at the edge."
+icon: SUSE.svg
+# Do not manually change any translations! See README.md for more details.
+translations:
+ description:
+ ca: Una opció de servidor de Linux oberta, fiable, compatible i a prova del
+ futur que garanteix la continuïtat del negoci de l'empresa. És el sistema
+ operatiu segur i adaptable per a una infraestructura amb suport a llarg
+ termini i preparada per a la innovació que executa càrregues de treball
+ crítiques per a l'empresa a les instal·lacions, al núvol i a l'última.
+ cs:
+ Otevřená, spolehlivá, kompatibilní a perspektivní volba linuxového serveru,
+ která zajišťuje kontinuitu podnikání podniku. Je to bezpečný a
+ přizpůsobivý operační systém pro dlouhodobě podporovanou infrastrukturu
+ připravenou na inovace, na které běží kritické podnikové úlohy v lokálním
+ prostředí, v cloudu i na okraji sítě.
+ es:
+ Una opción de servidor Linux abierta, confiable, compatible y preparada para
+ el futuro que garantiza la continuidad del negocio de la empresa. Es el
+ sistema operativo seguro y adaptable para una infraestructura lista para
+ la innovación y con soporte a largo plazo que ejecuta cargas de trabajo
+ críticas para el negocio en las instalaciones, en la nube y en el borde.
+ ja:
+ オープンで信頼性が高く、各種の標準にも準拠し、将来性とビジネスの継続性を支援する Linux
+ サーバです。長期のサポートが提供されていることから、安全性と順応性に優れ、オンプレミスからクラウド、エッジ環境に至るまで、様々な場所で重要なビジネス処理をこなすことのできる革新性の高いインフラストラクチャです。
+ pt_BR:
+ Uma escolha de servidor Linux aberta, confiável, compatível e à prova do
+ futuro que garante a continuidade dos negócios da empresa. É o SO seguro e
+ adaptável para infraestrutura com suporte de longo prazo e pronta para
+ inovação, executando cargas de trabalho críticas para os negócios no
+ local, na nuvem e na borda.
+ sv: Ett öppet, pålitligt, kompatibelt och framtidssäkert Linux-serverval som
+ säkerställer företagets affärskontinuitet. Det är det säkra och
+ anpassningsbara operativsystemet för långsiktigt stödd, innovationsfärdig
+ infrastruktur som kör affärskritiska arbetsbelastningar på plats, i molnet
+ och vid kanten.
+ tr:
+ İşletmenin iş sürekliliğini garanti eden açık, güvenilir, uyumlu ve geleceğe
+ dönük bir Linux Sunucu seçeneği. Uzun vadeli desteklenen, inovasyona hazır
+ altyapı için güvenli ve uyarlanabilir işletim sistemidir. Şirket içinde,
+ bulutta ve uçta iş açısından kritik iş yüklerini çalıştırır.
+software:
+ installation_repositories: []
+ mandatory_patterns:
+ - base_traditional
+ optional_patterns: null # no optional pattern shared
+ user_patterns: []
+ mandatory_packages:
+ - NetworkManager
+ optional_packages: null
+ base_product: SLES
+ version: "15-6"
+
+security:
+ lsm: selinux
+ available_lsms:
+ selinux:
+ patterns:
+ - selinux
+ policy: enforcing
+ none:
+ patterns: null
+
+storage:
+ space_policy: delete
+ volumes:
+ - "/"
+ - "swap"
+ volume_templates:
+ - mount_path: "/"
+ filesystem: btrfs
+ btrfs:
+ snapshots: true
+ read_only: false
+ default_subvolume: "@"
+ subvolumes:
+ - path: home
+ - path: opt
+ - path: root
+ - path: srv
+ - path: usr/local
+ # Unified var subvolume - https://lists.opensuse.org/opensuse-packaging/2017-11/msg00017.html
+ - path: var
+ copy_on_write: false
+ # Architecture specific subvolumes
+ - path: boot/grub2/arm64-efi
+ archs: aarch64
+ - path: boot/grub2/arm-efi
+ archs: arm
+ - path: boot/grub2/i386-pc
+ archs: x86_64
+ - path: boot/grub2/powerpc-ieee1275
+ archs: ppc,!board_powernv
+ - path: boot/grub2/s390x-emu
+ archs: s390
+ - path: boot/grub2/x86_64-efi
+ archs: x86_64
+ - path: boot/grub2/riscv64-efi
+ archs: riscv64
+ size:
+ auto: true
+ outline:
+ required: true
+ filesystems:
+ - btrfs
+ - ext2
+ - ext3
+ - ext4
+ - xfs
+ auto_size:
+ base_min: 5 GiB
+ base_max: 15 GiB
+ snapshots_increment: 250%
+ max_fallback_for:
+ - "/home"
+ snapshots_configurable: true
+ - mount_path: "swap"
+ filesystem: swap
+ size:
+ min: 1 GiB
+ max: 2 GiB
+ outline:
+ required: false
+ filesystems:
+ - swap
+ - mount_path: "/home"
+ filesystem: xfs
+ size:
+ auto: false
+ min: 10 GiB
+ max: unlimited
+ outline:
+ required: false
+ filesystems:
+ - btrfs
+ - ext2
+ - ext3
+ - ext4
+ - xfs
+ - filesystem: xfs
+ size:
+ auto: false
+ min: 1 GiB
+ outline:
+ required: false
+ filesystems:
+ - btrfs
+ - ext2
+ - ext3
+ - ext4
+ - xfs
+ - vfat
From c593d0fe1a891cefa01564a5cd4f326a3be6d16f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Tue, 3 Dec 2024 22:16:08 +0000
Subject: [PATCH 002/147] fix(service): fix wrong method name
---
service/lib/agama/software/manager.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/service/lib/agama/software/manager.rb b/service/lib/agama/software/manager.rb
index 63f4675af5..f76046ea7a 100644
--- a/service/lib/agama/software/manager.rb
+++ b/service/lib/agama/software/manager.rb
@@ -339,7 +339,7 @@ def registration
# rubocop:disable Metrics/AbcSize
def add_service(service)
# init repos, so we are sure we operate on "/" and have GPG imported
- initialize_target_repos
+ initialize_target
# save repositories before refreshing added services (otherwise
# pkg-bindings will treat them as removed by the service refresh and
# unload them)
From fc4da539cf951d014524ec91d37520a138967864 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 11:36:02 +0000
Subject: [PATCH 003/147] feat(service): include registration requirement in
the product
---
service/lib/agama/dbus/software/product.rb | 5 +++--
service/lib/agama/software/product.rb | 9 +++++++++
service/lib/agama/software/product_builder.rb | 1 +
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/service/lib/agama/dbus/software/product.rb b/service/lib/agama/dbus/software/product.rb
index 53209ff0a3..db2838a944 100644
--- a/service/lib/agama/dbus/software/product.rb
+++ b/service/lib/agama/dbus/software/product.rb
@@ -58,8 +58,9 @@ def available_products
product.id,
product.display_name,
{
- "description" => product.localized_description,
- "icon" => product.icon
+ "description" => product.localized_description,
+ "icon" => product.icon,
+ "registration" => product.registration
}
]
end
diff --git a/service/lib/agama/software/product.rb b/service/lib/agama/software/product.rb
index 3fe27489a7..ff18bdc9f0 100644
--- a/service/lib/agama/software/product.rb
+++ b/service/lib/agama/software/product.rb
@@ -19,6 +19,8 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
+require "agama/registration"
+
module Agama
module Software
# Represents a product that Agama can install.
@@ -90,6 +92,12 @@ class Product
# @return [Array]
attr_accessor :user_patterns
+ # Determines if the product should be registered.
+ #
+ # @see Agama::Registration::Requirement
+ # @return [String]
+ attr_accessor :registration
+
# Product translations.
#
# @example
@@ -115,6 +123,7 @@ def initialize(id)
@optional_patterns = []
# nil = display all visible patterns, [] = display no patterns
@user_patterns = nil
+ @registration = Agama::Registration::Requirement::NOT_REQUIRED
@translations = {}
end
diff --git a/service/lib/agama/software/product_builder.rb b/service/lib/agama/software/product_builder.rb
index a9ff1d655b..1d5234682b 100644
--- a/service/lib/agama/software/product_builder.rb
+++ b/service/lib/agama/software/product_builder.rb
@@ -65,6 +65,7 @@ def initialize_product(id, data, attrs)
product.name = data[:name]
product.version = data[:version]
product.icon = attrs["icon"] if attrs["icon"]
+ product.registration = attrs["registration"] if attrs["registration"]
end
end
From 68dad4ceba1ffc2f72665c53742cf7fa6a1909cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 11:41:48 +0000
Subject: [PATCH 004/147] feat(rust): include registration requirement in the
product
---
rust/agama-lib/src/product/client.rs | 17 +++++++------
rust/agama-lib/src/software/model.rs | 35 +++++++++------------------
rust/agama-server/src/software/web.rs | 26 --------------------
3 files changed, 22 insertions(+), 56 deletions(-)
diff --git a/rust/agama-lib/src/product/client.rs b/rust/agama-lib/src/product/client.rs
index 8517ddb9e9..4e6080b4f0 100644
--- a/rust/agama-lib/src/product/client.rs
+++ b/rust/agama-lib/src/product/client.rs
@@ -19,7 +19,9 @@
// find current contact information at www.suse.com.
use std::collections::HashMap;
+use std::str::FromStr;
+use crate::dbus::get_property;
use crate::error::ServiceError;
use crate::software::model::RegistrationRequirement;
use crate::software::proxies::SoftwareProductProxy;
@@ -39,6 +41,8 @@ pub struct Product {
pub description: String,
/// Product icon (e.g., "default.svg")
pub icon: String,
+ /// Registration requirement
+ pub registration: RegistrationRequirement,
}
/// D-Bus client for the software service
@@ -72,11 +76,17 @@ impl<'a> ProductClient<'a> {
Some(value) => value.try_into().unwrap(),
None => "default.svg",
};
+
+ let registration = get_property::(&data, "registration")
+ .map(|r| RegistrationRequirement::from_str(&r).unwrap_or_default())
+ .unwrap_or_default();
+
Product {
id,
name,
description: description.to_string(),
icon: icon.to_string(),
+ registration,
}
})
.collect();
@@ -114,13 +124,6 @@ impl<'a> ProductClient<'a> {
Ok(self.registration_proxy.email().await?)
}
- pub async fn registration_requirement(&self) -> Result {
- let requirement = self.registration_proxy.requirement().await?;
- // unknown number can happen only if we do programmer mistake
- let result: RegistrationRequirement = requirement.try_into().unwrap();
- Ok(result)
- }
-
/// register product
pub async fn register(&self, code: &str, email: &str) -> Result<(u32, String), ServiceError> {
let mut options: HashMap<&str, &zbus::zvariant::Value> = HashMap::new();
diff --git a/rust/agama-lib/src/software/model.rs b/rust/agama-lib/src/software/model.rs
index 31f81fbcaf..619b533a8f 100644
--- a/rust/agama-lib/src/software/model.rs
+++ b/rust/agama-lib/src/software/model.rs
@@ -47,14 +47,22 @@ pub struct RegistrationInfo {
pub key: String,
/// Registration email. Empty value mean email not used or not registered.
pub email: String,
- /// if registration is required, optional or not needed for current product.
- /// Change only if selected product is changed.
- pub requirement: RegistrationRequirement,
}
-#[derive(Clone, Debug, Serialize, Deserialize, utoipa::ToSchema)]
+#[derive(
+ Clone,
+ Default,
+ Debug,
+ Serialize,
+ Deserialize,
+ strum::Display,
+ strum::EnumString,
+ utoipa::ToSchema,
+)]
+#[strum(serialize_all = "camelCase")]
pub enum RegistrationRequirement {
/// Product does not require registration
+ #[default]
NotRequired = 0,
/// Product has optional registration
Optional = 1,
@@ -62,25 +70,6 @@ pub enum RegistrationRequirement {
Mandatory = 2,
}
-impl TryFrom for RegistrationRequirement {
- type Error = ();
-
- fn try_from(v: u32) -> Result {
- match v {
- x if x == RegistrationRequirement::NotRequired as u32 => {
- Ok(RegistrationRequirement::NotRequired)
- }
- x if x == RegistrationRequirement::Optional as u32 => {
- Ok(RegistrationRequirement::Optional)
- }
- x if x == RegistrationRequirement::Mandatory as u32 => {
- Ok(RegistrationRequirement::Mandatory)
- }
- _ => Err(()),
- }
- }
-}
-
/// Software resolvable type (package or pattern).
#[derive(Deserialize, Serialize, strum::Display, utoipa::ToSchema)]
#[strum(serialize_all = "camelCase")]
diff --git a/rust/agama-server/src/software/web.rs b/rust/agama-server/src/software/web.rs
index f4a0532349..f800c75cab 100644
--- a/rust/agama-server/src/software/web.rs
+++ b/rust/agama-server/src/software/web.rs
@@ -74,10 +74,6 @@ pub async fn software_streams(dbus: zbus::Connection) -> Result Result, Error> {
- // TODO: move registration requirement to product in dbus and so just one event will be needed.
- let proxy = RegistrationProxy::new(&dbus).await?;
- let stream = proxy
- .receive_requirement_changed()
- .await
- .then(|change| async move {
- if let Ok(id) = change.get().await {
- // unwrap is safe as possible numbers is send by our controlled dbus
- return Some(Event::RegistrationRequirementChanged {
- requirement: id.try_into().unwrap(),
- });
- }
- None
- })
- .filter_map(|e| e);
- Ok(stream)
-}
-
async fn registration_email_changed_stream(
dbus: zbus::Connection,
) -> Result, Error> {
@@ -269,7 +244,6 @@ async fn get_registration(
let result = RegistrationInfo {
key: state.product.registration_code().await?,
email: state.product.email().await?,
- requirement: state.product.registration_requirement().await?,
};
Ok(Json(result))
}
From fbbb7a74b522275d4612095de69206a1ab5db2e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 11:50:11 +0000
Subject: [PATCH 005/147] feat(products): add registration requirement to SLE
15 SP6
---
products.d/sles_156.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/products.d/sles_156.yaml b/products.d/sles_156.yaml
index cf242e68a0..ea22e9266e 100644
--- a/products.d/sles_156.yaml
+++ b/products.d/sles_156.yaml
@@ -1,5 +1,6 @@
id: SLES
name: SUSE Linux Enterprise Server 15.6
+registration: "mandatory"
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
From d0beb0b007cbd6931b49db871e9df2fccd0b3666 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 12:12:16 +0000
Subject: [PATCH 006/147] feat(service): move the "version" field to the
top-level
---
products.d/sles_156.yaml | 2 +-
service/lib/agama/software/product_builder.rb | 2 +-
.../agama/software/product_builder_test.rb | 22 +++++++++++--------
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/products.d/sles_156.yaml b/products.d/sles_156.yaml
index ea22e9266e..48c70ca4af 100644
--- a/products.d/sles_156.yaml
+++ b/products.d/sles_156.yaml
@@ -1,6 +1,7 @@
id: SLES
name: SUSE Linux Enterprise Server 15.6
registration: "mandatory"
+version: "15-6"
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
@@ -60,7 +61,6 @@ software:
- NetworkManager
optional_packages: null
base_product: SLES
- version: "15-6"
security:
lsm: selinux
diff --git a/service/lib/agama/software/product_builder.rb b/service/lib/agama/software/product_builder.rb
index 1d5234682b..a2507507bd 100644
--- a/service/lib/agama/software/product_builder.rb
+++ b/service/lib/agama/software/product_builder.rb
@@ -66,6 +66,7 @@ def initialize_product(id, data, attrs)
product.version = data[:version]
product.icon = attrs["icon"] if attrs["icon"]
product.registration = attrs["registration"] if attrs["registration"]
+ product.version = attrs["version"] if attrs["version"]
end
end
@@ -99,7 +100,6 @@ def set_translations(product, attrs)
def product_data_from_config(id)
{
name: config.products.dig(id, "software", "base_product"),
- version: config.products.dig(id, "software", "version"),
icon: config.products.dig(id, "software", "icon"),
labels: config.arch_elements_from(
id, "software", "installation_labels", property: :label
diff --git a/service/test/agama/software/product_builder_test.rb b/service/test/agama/software/product_builder_test.rb
index a48bc87ceb..cf67c02b0f 100644
--- a/service/test/agama/software/product_builder_test.rb
+++ b/service/test/agama/software/product_builder_test.rb
@@ -41,6 +41,8 @@
"id" => "Test1",
"name" => "Product Test 1",
"description" => "This is a test product named Test 1",
+ "version" => "1.0",
+ "registration" => "mandatory",
"translations" => {
"description" => {
"cs" => "Czech",
@@ -84,19 +86,19 @@
"archs" => "aarch64"
}
],
- "base_product" => "Test1",
- "version" => "1.0"
+ "base_product" => "Test1"
}
},
{
- "id" => "Test2",
- "name" => "Product Test 2",
- "description" => "This is a test product named Test 2",
- "archs" => "x86_64,aarch64",
- "software" => {
+ "id" => "Test2",
+ "name" => "Product Test 2",
+ "description" => "This is a test product named Test 2",
+ "archs" => "x86_64,aarch64",
+ "version" => "2.0",
+ "registration" => "optional",
+ "software" => {
"mandatory_patterns" => ["pattern2-1"],
- "base_product" => "Test2",
- "version" => "2.0"
+ "base_product" => "Test2"
}
},
{
@@ -143,6 +145,7 @@
description: "This is a test product named Test 1",
name: "Test1",
version: "1.0",
+ registration: "mandatory",
repositories: ["https://repos/test1/x86_64/product/"],
mandatory_patterns: ["pattern1-1", "pattern1-2"],
optional_patterns: ["pattern1-3"],
@@ -200,6 +203,7 @@
description: "This is a test product named Test 2",
name: "Test2",
version: "2.0",
+ registration: "optional",
repositories: [],
mandatory_patterns: ["pattern2-1"],
optional_patterns: [],
From bf7d346da98964c6f7d61d88a151b6b6fc11b2e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 12:23:23 +0000
Subject: [PATCH 007/147] refactor(rust): change registration "not_required" to
"no"
---
rust/agama-lib/src/software/model.rs | 2 +-
service/lib/agama/dbus/software/product.rb | 2 +-
service/lib/agama/registration.rb | 6 +++---
service/lib/agama/software/product.rb | 2 +-
service/test/agama/dbus/software/product_test.rb | 2 +-
service/test/agama/registration_test.rb | 4 ++--
6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/rust/agama-lib/src/software/model.rs b/rust/agama-lib/src/software/model.rs
index 619b533a8f..1678334473 100644
--- a/rust/agama-lib/src/software/model.rs
+++ b/rust/agama-lib/src/software/model.rs
@@ -63,7 +63,7 @@ pub struct RegistrationInfo {
pub enum RegistrationRequirement {
/// Product does not require registration
#[default]
- NotRequired = 0,
+ No = 0,
/// Product has optional registration
Optional = 1,
/// It is mandatory to register the product
diff --git a/service/lib/agama/dbus/software/product.rb b/service/lib/agama/dbus/software/product.rb
index db2838a944..40b7d2b1fd 100644
--- a/service/lib/agama/dbus/software/product.rb
+++ b/service/lib/agama/dbus/software/product.rb
@@ -178,7 +178,7 @@ def register(reg_code, email: nil)
[1, "Product not selected yet"]
elsif backend.registration.reg_code
[2, "Product already registered"]
- elsif backend.registration.requirement == Agama::Registration::Requirement::NOT_REQUIRED
+ elsif backend.registration.requirement == Agama::Registration::Requirement::NO
[3, "Product does not require registration"]
else
connect_result(first_error_code: 4) do
diff --git a/service/lib/agama/registration.rb b/service/lib/agama/registration.rb
index 3466147215..bc180f1ccd 100644
--- a/service/lib/agama/registration.rb
+++ b/service/lib/agama/registration.rb
@@ -41,7 +41,7 @@ class Registration
attr_reader :email
module Requirement
- NOT_REQUIRED = :not_required
+ NO = :no
OPTIONAL = :optional
MANDATORY = :mandatory
end
@@ -142,10 +142,10 @@ def finish
#
# @return [Symbol] See {Requirement}.
def requirement
- return Requirement::NOT_REQUIRED unless product
+ return Requirement::NO unless product
return Requirement::MANDATORY if product.repositories.none?
- Requirement::NOT_REQUIRED
+ Requirement::NO
end
# Callbacks to be called when registration changes (e.g., a different product is selected).
diff --git a/service/lib/agama/software/product.rb b/service/lib/agama/software/product.rb
index ff18bdc9f0..01b219bf09 100644
--- a/service/lib/agama/software/product.rb
+++ b/service/lib/agama/software/product.rb
@@ -123,7 +123,7 @@ def initialize(id)
@optional_patterns = []
# nil = display all visible patterns, [] = display no patterns
@user_patterns = nil
- @registration = Agama::Registration::Requirement::NOT_REQUIRED
+ @registration = Agama::Registration::Requirement::NO
@translations = {}
end
diff --git a/service/test/agama/dbus/software/product_test.rb b/service/test/agama/dbus/software/product_test.rb
index 8ca3d45787..cadcdf8dae 100644
--- a/service/test/agama/dbus/software/product_test.rb
+++ b/service/test/agama/dbus/software/product_test.rb
@@ -158,7 +158,7 @@
end
context "if the registration is not required" do
- let(:requirement) { Agama::Registration::Requirement::NOT_REQUIRED }
+ let(:requirement) { Agama::Registration::Requirement::NO }
it "returns 0" do
expect(subject.requirement).to eq(0)
diff --git a/service/test/agama/registration_test.rb b/service/test/agama/registration_test.rb
index a2dd6731b8..05b7757fef 100644
--- a/service/test/agama/registration_test.rb
+++ b/service/test/agama/registration_test.rb
@@ -346,7 +346,7 @@
let(:product) { nil }
it "returns not required" do
- expect(subject.requirement).to eq(Agama::Registration::Requirement::NOT_REQUIRED)
+ expect(subject.requirement).to eq(Agama::Registration::Requirement::NO)
end
end
@@ -359,7 +359,7 @@
let(:repositories) { ["https://repo"] }
it "returns not required" do
- expect(subject.requirement).to eq(Agama::Registration::Requirement::NOT_REQUIRED)
+ expect(subject.requirement).to eq(Agama::Registration::Requirement::NO)
end
end
From 9aac42681f219db7e5ca7f4b1c9e01694c1a6086 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 13:35:02 +0000
Subject: [PATCH 008/147] chore(products): temporarily move SLE 15 definition
to openSUSE package
---
products.d/agama-products.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/products.d/agama-products.spec b/products.d/agama-products.spec
index 4db27c60a9..d252bd9b75 100644
--- a/products.d/agama-products.spec
+++ b/products.d/agama-products.spec
@@ -52,6 +52,7 @@ Definition of openSUSE products (Tumbleweed, Leap, MicroOS and Slowroll) for the
%{_datadir}/agama/products.d/tumbleweed.yaml
%{_datadir}/agama/products.d/leap_160.yaml
%{_datadir}/agama/products.d/slowroll.yaml
+%{_datadir}/agama/products.d/sles_160.yaml
%package sle
Summary: Definition of SLE products for the Agama installer.
@@ -66,7 +67,6 @@ Definition of SLE-based products (e.g., SUSE Linux Enterprise Server) for the Ag
%dir %{_datadir}/agama
%dir %{_datadir}/agama/products.d
%{_datadir}/agama/products.d/sles_156.yaml
-%{_datadir}/agama/products.d/sles_160.yaml
%{_datadir}/agama/products.d/sles_sap_160.yaml
%changelog
From 9906fc03ab61bc6e0dade90c0a9670bc835108c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 14:00:57 +0000
Subject: [PATCH 009/147] chore(products): swap SLE 15 and SLE 16 definitions
---
products.d/agama-products.spec | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/products.d/agama-products.spec b/products.d/agama-products.spec
index d252bd9b75..41aba34169 100644
--- a/products.d/agama-products.spec
+++ b/products.d/agama-products.spec
@@ -52,7 +52,7 @@ Definition of openSUSE products (Tumbleweed, Leap, MicroOS and Slowroll) for the
%{_datadir}/agama/products.d/tumbleweed.yaml
%{_datadir}/agama/products.d/leap_160.yaml
%{_datadir}/agama/products.d/slowroll.yaml
-%{_datadir}/agama/products.d/sles_160.yaml
+%{_datadir}/agama/products.d/sles_156.yaml
%package sle
Summary: Definition of SLE products for the Agama installer.
@@ -66,7 +66,7 @@ Definition of SLE-based products (e.g., SUSE Linux Enterprise Server) for the Ag
%license LICENSE
%dir %{_datadir}/agama
%dir %{_datadir}/agama/products.d
-%{_datadir}/agama/products.d/sles_156.yaml
+%{_datadir}/agama/products.d/sles_160.yaml
%{_datadir}/agama/products.d/sles_sap_160.yaml
%changelog
From 22b734dc66cfcb4c4ba590c38256bc7bb6252ce4 Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Wed, 4 Dec 2024 17:34:07 +0100
Subject: [PATCH 010/147] quick POC to fix registration:
---
service/lib/agama/registration.rb | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/service/lib/agama/registration.rb b/service/lib/agama/registration.rb
index bc180f1ccd..7d50e9c401 100644
--- a/service/lib/agama/registration.rb
+++ b/service/lib/agama/registration.rb
@@ -30,6 +30,15 @@
module Agama
# Handles everything related to registration of system to SCC, RMT or similar.
class Registration
+
+ # Note: identical and keep in sync with Software::Manager::TARGET_DIR
+ TARGET_DIR = "/run/agama/zypp"
+ private_constant :TARGET_DIR
+
+ GLOBAL_CREDENTIALS_PATH = File.join(TARGET_DIR,
+ SUSE::Connect::YaST::GLOBAL_CREDENTIALS_FILE)
+ private_constant :GLOBAL_CREDENTIALS_PATH
+
# Code used for registering the product.
#
# @return [String, nil] nil if the product is not registered yet.
@@ -74,7 +83,7 @@ def register(code, email: "")
login, password = SUSE::Connect::YaST.announce_system(connect_params, target_distro)
# write the global credentials
# TODO: check if we can do it in memory for libzypp
- SUSE::Connect::YaST.create_credentials_file(login, password)
+ SUSE::Connect::YaST.create_credentials_file(login, password, GLOBAL_CREDENTIALS_PATH)
target_product = OpenStruct.new(
arch: Yast::Arch.rpm_arch,
@@ -86,7 +95,7 @@ def register(code, email: "")
# if service require specific credentials file, store it
@credentials_file = credentials_from_url(@service.url)
if @credentials_file
- SUSE::Connect::YaST.create_credentials_file(login, password, @credentials_file)
+ SUSE::Connect::YaST.create_credentials_file(login, password, File.join(TARGET_DIR, @credentials_file))
end
Y2Packager::NewRepositorySetup.instance.add_service(@service.name)
@software.add_service(@service)
@@ -116,9 +125,9 @@ def deregister
email: email
}
SUSE::Connect::YaST.deactivate_system(connect_params)
- FileUtils.rm(SUSE::Connect::YaST::GLOBAL_CREDENTIALS_FILE) # connect does not remove it itself
+ FileUtils.rm(GLOBAL_CREDENTIALS_PATH) # connect does not remove it itself
if @credentials_file
- FileUtils.rm(credentials_path(@credentials_file))
+ FileUtils.rm(credentials_path(File.join(TARGET_DIR, @credentials_file)))
@credentials_file = nil
end
@@ -131,7 +140,7 @@ def deregister
def finish
return unless reg_code
- files = [credentials_path(@credentials_file), SUSE::Connect::YaST::GLOBAL_CREDENTIALS_FILE]
+ files = [credentials_path(@credentials_file), SUSE::Connect::YaST::GLOBAL_CREDENTIALS_PATH]
files.each do |file|
dest = File.join(Yast::Installation.destdir, file)
FileUtils.cp(file, dest)
From b9c01551b418e06af6eea7a8d0a37c2c4d6565bb Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Wed, 4 Dec 2024 17:41:35 +0100
Subject: [PATCH 011/147] make rubocop happy and fix tests
---
service/lib/agama/registration.rb | 6 +++---
service/test/agama/registration_test.rb | 7 ++++---
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/service/lib/agama/registration.rb b/service/lib/agama/registration.rb
index 7d50e9c401..c3618c70c5 100644
--- a/service/lib/agama/registration.rb
+++ b/service/lib/agama/registration.rb
@@ -30,8 +30,7 @@
module Agama
# Handles everything related to registration of system to SCC, RMT or similar.
class Registration
-
- # Note: identical and keep in sync with Software::Manager::TARGET_DIR
+ # NOTE: identical and keep in sync with Software::Manager::TARGET_DIR
TARGET_DIR = "/run/agama/zypp"
private_constant :TARGET_DIR
@@ -95,7 +94,8 @@ def register(code, email: "")
# if service require specific credentials file, store it
@credentials_file = credentials_from_url(@service.url)
if @credentials_file
- SUSE::Connect::YaST.create_credentials_file(login, password, File.join(TARGET_DIR, @credentials_file))
+ SUSE::Connect::YaST.create_credentials_file(login, password,
+ File.join(TARGET_DIR, @credentials_file))
end
Y2Packager::NewRepositorySetup.instance.add_service(@service.name)
@software.add_service(@service)
diff --git a/service/test/agama/registration_test.rb b/service/test/agama/registration_test.rb
index 05b7757fef..72b79d2323 100644
--- a/service/test/agama/registration_test.rb
+++ b/service/test/agama/registration_test.rb
@@ -90,7 +90,7 @@
it "creates credentials file" do
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
- .with("test-user", "12345")
+ .with("test-user", "12345", "/run/agama/zypp/etc/zypp/credentials.d/SCCcredentials")
subject.register("11112222", email: "test@test.com")
end
@@ -117,13 +117,14 @@
before do
allow(subject).to receive(:credentials_from_url)
- .with("https://credentials/file").and_return("credentials")
+ .with("https://credentials/file")
+ .and_return("/etc/zypp/credentials.d/product")
end
it "creates the credentials file" do
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
- .with("test-user", "12345", "credentials")
+ .with("test-user", "12345", "/run/agama/zypp/etc/zypp/credentials.d/product")
subject.register("11112222", email: "test@test.com")
end
From 3e3708c8c8f7bdce297a37ccb747a4ce98e03848 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Wed, 4 Dec 2024 22:00:47 +0000
Subject: [PATCH 012/147] fix(web): add registration to Product type
---
web/src/App.test.tsx | 4 ++--
web/src/components/core/ChangeProductLink.test.tsx | 2 ++
web/src/components/layout/Header.test.tsx | 7 +++++--
web/src/components/product/ProductSelectionPage.test.tsx | 2 ++
web/src/types/software.ts | 2 ++
5 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/web/src/App.test.tsx b/web/src/App.test.tsx
index 1f806f0989..abe0834187 100644
--- a/web/src/App.test.tsx
+++ b/web/src/App.test.tsx
@@ -40,8 +40,8 @@ jest.mock("~/api/l10n", () => ({
updateConfig: jest.fn(),
}));
-const tumbleweed: Product = { id: "openSUSE", name: "openSUSE Tumbleweed" };
-const microos: Product = { id: "Leap Micro", name: "openSUSE Micro" };
+const tumbleweed: Product = { id: "openSUSE", name: "openSUSE Tumbleweed", registration: "No" };
+const microos: Product = { id: "Leap Micro", name: "openSUSE Micro", registration: "No" };
// list of available products
let mockProducts: Product[];
diff --git a/web/src/components/core/ChangeProductLink.test.tsx b/web/src/components/core/ChangeProductLink.test.tsx
index ddbdd369d1..bc71c1be9b 100644
--- a/web/src/components/core/ChangeProductLink.test.tsx
+++ b/web/src/components/core/ChangeProductLink.test.tsx
@@ -32,12 +32,14 @@ const tumbleweed: Product = {
name: "openSUSE Tumbleweed",
icon: "tumbleweed.svg",
description: "Tumbleweed description...",
+ registration: "No",
};
const microos: Product = {
id: "MicroOS",
name: "openSUSE MicroOS",
icon: "MicroOS.svg",
description: "MicroOS description",
+ registration: "No",
};
let mockUseProduct: { products: Product[]; selectedProduct?: Product };
diff --git a/web/src/components/layout/Header.test.tsx b/web/src/components/layout/Header.test.tsx
index 1ae19803fc..882a11e904 100644
--- a/web/src/components/layout/Header.test.tsx
+++ b/web/src/components/layout/Header.test.tsx
@@ -25,17 +25,20 @@ import { screen, within } from "@testing-library/react";
import { installerRender, mockRoutes } from "~/test-utils";
import Header from "./Header";
import { InstallationPhase } from "~/types/status";
+import { Product } from "~/types/software";
-const tumbleweed = {
+const tumbleweed: Product = {
id: "Tumbleweed",
name: "openSUSE Tumbleweed",
description: "Tumbleweed description...",
+ registration: "No",
};
-const microos = {
+const microos: Product = {
id: "MicroOS",
name: "openSUSE MicroOS",
description: "MicroOS description",
+ registration: "No",
};
let phase: InstallationPhase;
diff --git a/web/src/components/product/ProductSelectionPage.test.tsx b/web/src/components/product/ProductSelectionPage.test.tsx
index 4209e2da58..bb096ef626 100644
--- a/web/src/components/product/ProductSelectionPage.test.tsx
+++ b/web/src/components/product/ProductSelectionPage.test.tsx
@@ -33,6 +33,7 @@ const tumbleweed: Product = {
name: "openSUSE Tumbleweed",
icon: "tumbleweed.svg",
description: "Tumbleweed description...",
+ registration: "No",
};
const microOs: Product = {
@@ -40,6 +41,7 @@ const microOs: Product = {
name: "openSUSE MicroOS",
icon: "microos.svg",
description: "MicroOS description",
+ registration: "No",
};
jest.mock("~/queries/software", () => ({
diff --git a/web/src/types/software.ts b/web/src/types/software.ts
index 822d60855a..6a54c0b429 100644
--- a/web/src/types/software.ts
+++ b/web/src/types/software.ts
@@ -41,6 +41,8 @@ type Product = {
description?: string;
/** Product icon (e.g., "default.svg") */
icon?: string;
+ /** If product is registrable or not */
+ registration: string;
};
type PatternsSelection = { [key: string]: SelectedBy };
From 56fa368c6fab9dc8e61652d45290dcf12c4d6f70 Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Wed, 4 Dec 2024 23:02:59 +0100
Subject: [PATCH 013/147] fix creating credentials
---
service/lib/agama/registration.rb | 4 ++--
service/test/agama/registration_test.rb | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/service/lib/agama/registration.rb b/service/lib/agama/registration.rb
index c3618c70c5..208c8290ed 100644
--- a/service/lib/agama/registration.rb
+++ b/service/lib/agama/registration.rb
@@ -95,7 +95,7 @@ def register(code, email: "")
@credentials_file = credentials_from_url(@service.url)
if @credentials_file
SUSE::Connect::YaST.create_credentials_file(login, password,
- File.join(TARGET_DIR, @credentials_file))
+ File.join(TARGET_DIR, credentials_path(@credentials_file)))
end
Y2Packager::NewRepositorySetup.instance.add_service(@service.name)
@software.add_service(@service)
@@ -127,7 +127,7 @@ def deregister
SUSE::Connect::YaST.deactivate_system(connect_params)
FileUtils.rm(GLOBAL_CREDENTIALS_PATH) # connect does not remove it itself
if @credentials_file
- FileUtils.rm(credentials_path(File.join(TARGET_DIR, @credentials_file)))
+ FileUtils.rm(File.join(TARGET_DIR, credentials_path(@credentials_file)))
@credentials_file = nil
end
diff --git a/service/test/agama/registration_test.rb b/service/test/agama/registration_test.rb
index 72b79d2323..d34255c2ba 100644
--- a/service/test/agama/registration_test.rb
+++ b/service/test/agama/registration_test.rb
@@ -118,13 +118,13 @@
before do
allow(subject).to receive(:credentials_from_url)
.with("https://credentials/file")
- .and_return("/etc/zypp/credentials.d/product")
+ .and_return("productA")
end
it "creates the credentials file" do
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
- .with("test-user", "12345", "/run/agama/zypp/etc/zypp/credentials.d/product")
+ .with("test-user", "12345", "/run/agama/zypp/etc/zypp/credentials.d/productA")
subject.register("11112222", email: "test@test.com")
end
From 916d41e99dbacd3b3393d4b0730e638d2960fbca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Wed, 4 Dec 2024 22:03:47 +0000
Subject: [PATCH 014/147] feat(web): allow setting a route only for registrable
products
Actually, at this moment the flag is only used for including a link to
the route in the main navigation or not depending on the
Product#registration value. But the idea is to limite the navigation too
in future iterations.
---
web/src/components/layout/Sidebar.test.tsx | 66 +++++++++++++++++++---
web/src/components/layout/Sidebar.tsx | 4 ++
web/src/types/routes.ts | 2 +
3 files changed, 65 insertions(+), 7 deletions(-)
diff --git a/web/src/components/layout/Sidebar.test.tsx b/web/src/components/layout/Sidebar.test.tsx
index fa3d3c7495..346bf95317 100644
--- a/web/src/components/layout/Sidebar.test.tsx
+++ b/web/src/components/layout/Sidebar.test.tsx
@@ -24,25 +24,77 @@ import React from "react";
import { screen, within } from "@testing-library/react";
import { installerRender } from "~/test-utils";
import Sidebar from "./Sidebar";
+import { Product } from "~/types/software";
+import { useProduct } from "~/queries/software";
jest.mock("~/components/core/ChangeProductLink", () => () =>
ChangeProductLink Mock
);
+const tw: Product = {
+ id: "Tumbleweed",
+ name: "openSUSE Tumbleweed",
+ registration: "No",
+};
+
+const sle: Product = {
+ id: "sle",
+ name: "SLE",
+ registration: "Mandatory",
+};
+
+let selectedProduct: Product;
+
+jest.mock("~/queries/software", () => ({
+ ...jest.requireActual("~/queries/software"),
+ useProduct: (): ReturnType => {
+ return {
+ products: [tw, sle],
+ selectedProduct,
+ };
+ },
+}));
+
jest.mock("~/router", () => ({
rootRoutes: () => [
{ path: "/", handle: { name: "Main" } },
{ path: "/l10n", handle: { name: "L10n" } },
{ path: "/hidden" },
+ {
+ path: "/registration",
+ handle: { name: "Registration", needsRegistrableProduct: true },
+ },
],
}));
describe("Sidebar", () => {
- it("renders a navigation on top of root routes with handle object", () => {
- installerRender();
- const mainNavigation = screen.getByRole("navigation");
- const mainNavigationLinks = within(mainNavigation).getAllByRole("link");
- expect(mainNavigationLinks.length).toBe(2);
- screen.getByRole("link", { name: "Main" });
- screen.getByRole("link", { name: "L10n" });
+ describe("when product is registrable", () => {
+ beforeEach(() => {
+ selectedProduct = sle;
+ });
+
+ it("renders a navigation including all root routes with handle object", () => {
+ installerRender();
+ const mainNavigation = screen.getByRole("navigation");
+ const mainNavigationLinks = within(mainNavigation).getAllByRole("link");
+ expect(mainNavigationLinks.length).toBe(3);
+ screen.getByRole("link", { name: "Main" });
+ screen.getByRole("link", { name: "L10n" });
+ screen.getByRole("link", { name: "Registration" });
+ });
+ });
+
+ describe("when product is not registrable", () => {
+ beforeEach(() => {
+ selectedProduct = tw;
+ });
+
+ it("renders a navigation including all root routes with handle object, except ones set as needsRegistrableProduct", () => {
+ installerRender();
+ const mainNavigation = screen.getByRole("navigation");
+ const mainNavigationLinks = within(mainNavigation).getAllByRole("link");
+ expect(mainNavigationLinks.length).toBe(2);
+ screen.getByRole("link", { name: "Main" });
+ screen.getByRole("link", { name: "L10n" });
+ });
});
it("mounts core/ChangeProductLink component", () => {
diff --git a/web/src/components/layout/Sidebar.tsx b/web/src/components/layout/Sidebar.tsx
index aa768d0218..2b1789c21d 100644
--- a/web/src/components/layout/Sidebar.tsx
+++ b/web/src/components/layout/Sidebar.tsx
@@ -27,10 +27,14 @@ import { Icon } from "~/components/layout";
import { ChangeProductLink } from "~/components/core";
import { rootRoutes } from "~/router";
import { _ } from "~/i18n";
+import { useProduct } from "~/queries/software";
const MainNavigation = (): React.ReactNode => {
+ const { selectedProduct: product } = useProduct();
+
const links = rootRoutes().map((r) => {
if (!r.handle) return null;
+ if (r.handle.needsRegistrableProduct && product.registration === "No") return null;
// eslint-disable-next-line agama-i18n/string-literals
const name = _(r.handle.name);
diff --git a/web/src/types/routes.ts b/web/src/types/routes.ts
index 11f2135547..fe5b85ea71 100644
--- a/web/src/types/routes.ts
+++ b/web/src/types/routes.ts
@@ -29,6 +29,8 @@ type RouteHandle = {
title?: string;
/** Icon for representing the route in some places, like a menu entry */
icon?: string;
+ /** Whether the route link will be rendered for registrable products only */
+ needsRegistrableProduct?: boolean;
};
type Route = RouteObject & { handle?: RouteHandle };
From 34d311db505410ecf082e658c5d19aa1828ca653 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Wed, 4 Dec 2024 22:15:51 +0000
Subject: [PATCH 015/147] feat(web): add product registration interface
An initial version that basically consists on
- Main menu entry for registration
- Global alert visible on all pages except the registration page and
those mounted at "supportive paths" (temporary name)
- Section with form for entering registration code and email
- Section to display registration information when available
Only basic workflow has been manually tested; unit tests will be added in upcoming commits.
---
web/src/api/software.ts | 37 +++-
web/src/components/core/IssuesDrawer.tsx | 7 +-
web/src/components/core/Page.tsx | 10 +-
web/src/components/layout/Icon.tsx | 2 +
.../product/ProductRegistrationAlert.tsx | 58 ++++++
.../product/ProductRegistrationPage.tsx | 184 ++++++++++++++++++
web/src/components/product/index.ts | 2 +
web/src/queries/software.ts | 61 ++++++
web/src/router.js | 2 +
web/src/routes/paths.ts | 15 +-
web/src/routes/registration.tsx | 40 ++++
web/src/types/software.ts | 14 +-
web/src/utils.js | 6 +
13 files changed, 429 insertions(+), 9 deletions(-)
create mode 100644 web/src/components/product/ProductRegistrationAlert.tsx
create mode 100644 web/src/components/product/ProductRegistrationPage.tsx
create mode 100644 web/src/routes/registration.tsx
diff --git a/web/src/api/software.ts b/web/src/api/software.ts
index 3be92e58ff..82f8650dc5 100644
--- a/web/src/api/software.ts
+++ b/web/src/api/software.ts
@@ -20,8 +20,14 @@
* find current contact information at www.suse.com.
*/
-import { Pattern, Product, SoftwareConfig, SoftwareProposal } from "~/types/software";
-import { get, put } from "~/api/http";
+import {
+ Pattern,
+ Product,
+ SoftwareConfig,
+ RegistrationInfo,
+ SoftwareProposal,
+} from "~/types/software";
+import { del, get, post, put } from "~/api/http";
/**
* Returns the software configuration
@@ -38,6 +44,11 @@ const fetchProposal = (): Promise => get("/api/software/propos
*/
const fetchProducts = (): Promise => get("/api/software/products");
+/**
+ * Returns an object with the registration info
+ */
+const fetchRegistration = (): Promise => get("/api/software/registration");
+
/**
* Returns the list of patterns for the selected product
*/
@@ -50,4 +61,24 @@ const fetchPatterns = (): Promise => get("/api/software/patterns");
*/
const updateConfig = (config: SoftwareConfig) => put("/api/software/config", config);
-export { fetchConfig, fetchPatterns, fetchProposal, fetchProducts, updateConfig };
+/**
+ * Request registration of selected product with given key
+ */
+const register = ({ key, email }: { key: string; email?: string }) =>
+ post("/api/software/registration", { key, email });
+
+/**
+ * Request deregistering selected product
+ */
+const deregister = () => del("/api/software/registration");
+
+export {
+ fetchConfig,
+ fetchPatterns,
+ fetchProposal,
+ fetchProducts,
+ fetchRegistration,
+ updateConfig,
+ register,
+ deregister,
+};
diff --git a/web/src/components/core/IssuesDrawer.tsx b/web/src/components/core/IssuesDrawer.tsx
index 4a0ecfa7fe..67a8fdd94d 100644
--- a/web/src/components/core/IssuesDrawer.tsx
+++ b/web/src/components/core/IssuesDrawer.tsx
@@ -49,6 +49,8 @@ const IssuesDrawer = forwardRef(({ onClose }: { onClose: () => void }, ref) => {
users: _("Users"),
storage: _("Storage"),
software: _("Software"),
+ // FIXME: Registration or Product?
+ product: _("Registration"),
};
if (issues.isEmpty || phase === InstallationPhase.Install) return;
@@ -65,13 +67,16 @@ const IssuesDrawer = forwardRef(({ onClose }: { onClose: () => void }, ref) => {
{Object.entries(issuesByScope).map(([scope, issues], idx) => {
if (issues.length === 0) return null;
+ // FIXME: address this better or use the /product(s)? namespace instead of
+ // /registration.
+ const section = scope === "product" ? "registration" : scope;
const ariaLabelId = `${scope}-issues-section`;
return (
-
+
{scopeHeaders[scope]}
diff --git a/web/src/components/core/Page.tsx b/web/src/components/core/Page.tsx
index 799f2413eb..5ddf83e5fa 100644
--- a/web/src/components/core/Page.tsx
+++ b/web/src/components/core/Page.tsx
@@ -40,6 +40,7 @@ import {
TitleProps,
} from "@patternfly/react-core";
import { Flex } from "~/components/layout";
+import { ProductRegistrationAlert } from "~/components/product";
import { _ } from "~/i18n";
import textStyles from "@patternfly/react-styles/css/utilities/Text/text";
import flexStyles from "@patternfly/react-styles/css/utilities/Flex/flex";
@@ -279,9 +280,12 @@ const Submit = ({ children, ...props }: SubmitActionProps) => {
* @see [Patternfly Page/PageSection](https://www.patternfly.org/components/page#pagesection)
*/
const Content = ({ children, ...pageSectionProps }: React.PropsWithChildren) => (
-
- {children}
-
+ <>
+
+
+ {children}
+
+ >
);
/**
diff --git a/web/src/components/layout/Icon.tsx b/web/src/components/layout/Icon.tsx
index cbdc3ce110..09ab2d872b 100644
--- a/web/src/components/layout/Icon.tsx
+++ b/web/src/components/layout/Icon.tsx
@@ -26,6 +26,7 @@ import React from "react";
// icons location. Check the tsconfig.json file to see its value.
import AddAPhoto from "@icons/add_a_photo.svg?component";
import Apps from "@icons/apps.svg?component";
+import AppRegistration from "@icons/app_registration.svg?component";
import Badge from "@icons/badge.svg?component";
import Backspace from "@icons/backspace.svg?component";
import CheckCircle from "@icons/check_circle.svg?component";
@@ -91,6 +92,7 @@ import { SiLinux } from "@icons-pack/react-simple-icons";
const icons = {
add_a_photo: AddAPhoto,
apps: Apps,
+ app_registration: AppRegistration,
badge: Badge,
backspace: Backspace,
check_circle: CheckCircle,
diff --git a/web/src/components/product/ProductRegistrationAlert.tsx b/web/src/components/product/ProductRegistrationAlert.tsx
new file mode 100644
index 0000000000..1681daaf92
--- /dev/null
+++ b/web/src/components/product/ProductRegistrationAlert.tsx
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) [2023-2024] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+import React from "react";
+import { Alert, Flex } from "@patternfly/react-core";
+import { useLocation } from "react-router-dom";
+import { Link } from "~/components/core";
+import { useProduct, useRegistration } from "~/queries/software";
+import { REGISTRATION, SUPPORTIVE_PATHS } from "~/routes/paths";
+import { isEmpty } from "~/utils";
+import { _ } from "~/i18n";
+import { sprintf } from "sprintf-js";
+
+export default function ProductRegistrationAlert() {
+ const location = useLocation();
+ const { selectedProduct: product } = useProduct();
+ const registration = useRegistration();
+
+ if ([...SUPPORTIVE_PATHS, REGISTRATION.root].includes(location.pathname)) return;
+ if (product.registration === "No" || !isEmpty(registration.key)) return;
+
+ return (
+
+
+
+ {sprintf(
+ _(
+ "%s has to be registered because ",
+ ),
+ product.name,
+ )}
+
+
+ {_("Register it now")}
+
+
+
+ );
+}
diff --git a/web/src/components/product/ProductRegistrationPage.tsx b/web/src/components/product/ProductRegistrationPage.tsx
new file mode 100644
index 0000000000..ca6ea4ed94
--- /dev/null
+++ b/web/src/components/product/ProductRegistrationPage.tsx
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) [2023-2024] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+import React, { useState } from "react";
+import {
+ ActionGroup,
+ Alert,
+ Button,
+ DescriptionList,
+ DescriptionListDescription,
+ DescriptionListGroup,
+ DescriptionListTerm,
+ Flex,
+ Form,
+ FormGroup,
+ Grid,
+ Stack,
+ TextInput,
+} from "@patternfly/react-core";
+import { Page, PasswordInput } from "~/components/core";
+import textStyles from "@patternfly/react-styles/css/utilities/Text/text";
+import spacingStyles from "@patternfly/react-styles/css/utilities/Spacing/spacing";
+import {
+ useProduct,
+ useRegistration,
+ useRegisterMutation,
+ useDeregisterMutation,
+} from "~/queries/software";
+import { isEmpty, mask } from "~/utils";
+import { _ } from "~/i18n";
+import { sprintf } from "sprintf-js";
+
+const FORM_ID = "productRegistration";
+const KEY_LABEL = _("Registration code");
+const EMAIL_LABEL = "Email";
+
+const RegisteredProductSection = () => {
+ const { selectedProduct: product } = useProduct();
+ const { mutate: deregister } = useDeregisterMutation();
+ const registration = useRegistration();
+ const [showCode, setShowCode] = useState(false);
+ const toggleCodeVisibility = () => setShowCode(!showCode);
+
+ const footer = _("For using a different registration code, please %s the product first.");
+ const deregisterButtonLabel = _("deregister");
+ const [footerStart, footerEnd] = footer.split("%s");
+
+ return (
+
+ {footerStart}{" "}
+ {" "}
+ {footerEnd}
+
+ }
+ >
+
+
+ {KEY_LABEL}
+
+
+ {showCode ? registration.key : mask(registration.key)}
+
+
+
+ {!isEmpty(registration.email) && (
+ <>
+ {EMAIL_LABEL}
+ {registration.email}
+ >
+ )}
+
+
+
+ );
+};
+
+const RegistrationFormSection = () => {
+ const { mutate: register } = useRegisterMutation();
+ const [key, setKey] = useState("");
+ const [email, setEmail] = useState("");
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(false);
+
+ // FIXME: use the right type for AxiosResponse
+ const onRegisterError = ({ response }) => {
+ const originalMessage = response.data.message;
+ const from = originalMessage.indexOf(":") + 1;
+ setError(originalMessage.slice(from).trim());
+ };
+
+ const submit = async (e: React.SyntheticEvent) => {
+ e.preventDefault();
+ setError(null);
+ setLoading(true);
+ register({ key, email }, { onError: onRegisterError, onSettled: () => setLoading(false) });
+ };
+
+ // TODO: adjust texts based of registration "type", mandatory or optional
+
+ return (
+
+
+
+ );
+};
+
+export default function ProductRegistrationPage() {
+ const { selectedProduct: product } = useProduct();
+ const registration = useRegistration();
+
+ // TODO: render something meaningful instead? "Product not registrable"?
+ if (product.registration === "No") return;
+
+ return (
+
+
+
{_("Registration")}
+
+
+
+
+
+ {isEmpty(registration.key) ? : }
+
+
+
+
+ );
+}
diff --git a/web/src/components/product/index.ts b/web/src/components/product/index.ts
index 1340569f37..fc63c951d0 100644
--- a/web/src/components/product/index.ts
+++ b/web/src/components/product/index.ts
@@ -22,3 +22,5 @@
export { default as ProductSelectionPage } from "./ProductSelectionPage";
export { default as ProductSelectionProgress } from "./ProductSelectionProgress";
+export { default as ProductRegistrationPage } from "./ProductRegistrationPage";
+export { default as ProductRegistrationAlert } from "./ProductRegistrationAlert";
diff --git a/web/src/queries/software.ts b/web/src/queries/software.ts
index 6030489bd7..287154283d 100644
--- a/web/src/queries/software.ts
+++ b/web/src/queries/software.ts
@@ -33,15 +33,19 @@ import {
Pattern,
PatternsSelection,
Product,
+ RegistrationInfo,
SelectedBy,
SoftwareConfig,
SoftwareProposal,
} from "~/types/software";
import {
+ deregister,
fetchConfig,
fetchPatterns,
fetchProducts,
fetchProposal,
+ fetchRegistration,
+ register,
updateConfig,
} from "~/api/software";
import { QueryHookOptions } from "~/types/queries";
@@ -80,6 +84,14 @@ const selectedProductQuery = () => ({
queryFn: () => fetchConfig().then(({ product }) => product),
});
+/**
+ * Query to retrieve registration info
+ */
+const registrationQuery = () => ({
+ queryKey: ["software/registration"],
+ queryFn: fetchRegistration,
+});
+
/**
* Query to retrieve available patterns
*/
@@ -111,6 +123,44 @@ const useConfigMutation = () => {
return useMutation(query);
};
+/**
+ * Hook that builds a mutation for registering a product
+ *
+ * @note it would trigger a general probing as a side-effect when mutation
+ * includes a product.
+ */
+const useRegisterMutation = () => {
+ const queryClient = useQueryClient();
+
+ const query = {
+ mutationFn: register,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["software/registration"] });
+ startProbing();
+ },
+ };
+ return useMutation(query);
+};
+
+/**
+ * Hook that builds a mutation for deregistering a product
+ *
+ * @note it would trigger a general probing as a side-effect when mutation
+ * includes a product.
+ */
+const useDeregisterMutation = () => {
+ const queryClient = useQueryClient();
+
+ const query = {
+ mutationFn: deregister,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["software/registration"] });
+ startProbing();
+ },
+ };
+ return useMutation(query);
+};
+
/**
* Returns available products and selected one, if any
*/
@@ -172,6 +222,14 @@ const useProposal = (): SoftwareProposal => {
return proposal;
};
+/**
+ * Returns registration info
+ */
+const useRegistration = (): RegistrationInfo => {
+ const { data: registration } = useSuspenseQuery(registrationQuery());
+ return registration;
+};
+
/**
* Hook that returns a useEffect to listen for software proposal events
*
@@ -226,4 +284,7 @@ export {
useProductChanges,
useProposal,
useProposalChanges,
+ useRegistration,
+ useRegisterMutation,
+ useDeregisterMutation,
};
diff --git a/web/src/router.js b/web/src/router.js
index f949a76652..314ef63116 100644
--- a/web/src/router.js
+++ b/web/src/router.js
@@ -30,6 +30,7 @@ import { OverviewPage } from "~/components/overview";
import l10nRoutes from "~/routes/l10n";
import networkRoutes from "~/routes/network";
import productsRoutes from "~/routes/products";
+import registrationRoutes from "~/routes/registration";
import storageRoutes from "~/routes/storage";
import softwareRoutes from "~/routes/software";
import usersRoutes from "~/routes/users";
@@ -43,6 +44,7 @@ const rootRoutes = () => [
element: ,
handle: { name: N_("Overview"), icon: "list_alt" },
},
+ registrationRoutes(),
l10nRoutes(),
networkRoutes(),
storageRoutes(),
diff --git a/web/src/routes/paths.ts b/web/src/routes/paths.ts
index 22cdba06e2..efe67522a4 100644
--- a/web/src/routes/paths.ts
+++ b/web/src/routes/paths.ts
@@ -39,6 +39,10 @@ const PRODUCT = {
progress: "/products/progress",
};
+const REGISTRATION = {
+ root: "/registration",
+};
+
const ROOT = {
root: "/",
login: "/login",
@@ -78,4 +82,13 @@ const STORAGE = {
},
};
-export { L10N, NETWORK, PRODUCT, ROOT, SOFTWARE, STORAGE, USER };
+const SUPPORTIVE_PATHS = [
+ ROOT.login,
+ PRODUCT.changeProduct,
+ PRODUCT.progress,
+ ROOT.installationProgress,
+ ROOT.installationFinished,
+ USER.rootUser.edit,
+];
+
+export { L10N, NETWORK, PRODUCT, REGISTRATION, ROOT, SOFTWARE, STORAGE, USER, SUPPORTIVE_PATHS };
diff --git a/web/src/routes/registration.tsx b/web/src/routes/registration.tsx
new file mode 100644
index 0000000000..db27ad5037
--- /dev/null
+++ b/web/src/routes/registration.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) [2024] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+import React from "react";
+import { ProductRegistrationPage } from "~/components/product";
+import { Route } from "~/types/routes";
+import { REGISTRATION as PATHS } from "~/routes/paths";
+import { N_ } from "~/i18n";
+
+const routes = (): Route => ({
+ path: PATHS.root,
+ handle: { name: N_("Registration"), icon: "app_registration", needsRegistrableProduct: true },
+ children: [
+ {
+ index: true,
+ element: ,
+ },
+ ],
+});
+
+export default routes;
diff --git a/web/src/types/software.ts b/web/src/types/software.ts
index 6a54c0b429..59f5dcaeef 100644
--- a/web/src/types/software.ts
+++ b/web/src/types/software.ts
@@ -78,5 +78,17 @@ type Pattern = {
selectedBy?: SelectedBy;
};
+type RegistrationInfo = {
+ key: string;
+ email?: string;
+};
+
export { SelectedBy };
-export type { Pattern, PatternsSelection, Product, SoftwareConfig, SoftwareProposal };
+export type {
+ Pattern,
+ PatternsSelection,
+ Product,
+ SoftwareConfig,
+ RegistrationInfo,
+ SoftwareProposal,
+};
diff --git a/web/src/utils.js b/web/src/utils.js
index 50feed2b79..418de947a1 100644
--- a/web/src/utils.js
+++ b/web/src/utils.js
@@ -420,6 +420,11 @@ const timezoneTime = (timezone, { date = new Date() }) => {
}
};
+const mask = (value, visible = 4, character = "*") => {
+ const regex = new RegExp(`.(?=(.{${visible}}))`, "g");
+ return value.replace(regex, character);
+};
+
export {
noop,
identity,
@@ -441,4 +446,5 @@ export {
remoteConnection,
slugify,
timezoneTime,
+ mask,
};
From 0063a08dc465e603e3d9aa2f0fc6ce876af86f10 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Wed, 4 Dec 2024 22:30:49 +0000
Subject: [PATCH 016/147] fix(web): use "supportive paths" for InstallButton
Certain elements must not be rendered on paths that do not use the
"FullLayout". Other paths, considered "accessory" paths, have been
temporarily grouped under the "SUPPORTIVE_PATHS" constant to reduce code
duplication when excluding elements from them.
Although the approach needs improvement, starting with the naming,
let's use it where needed meanwhile.
---
web/src/components/core/InstallButton.tsx | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/web/src/components/core/InstallButton.tsx b/web/src/components/core/InstallButton.tsx
index 5a6d44333d..93826fe188 100644
--- a/web/src/components/core/InstallButton.tsx
+++ b/web/src/components/core/InstallButton.tsx
@@ -26,7 +26,7 @@ import { Popup } from "~/components/core";
import { startInstallation } from "~/api/manager";
import { useAllIssues } from "~/queries/issues";
import { useLocation } from "react-router-dom";
-import { PRODUCT, ROOT, USER } from "~/routes/paths";
+import { SUPPORTIVE_PATHS } from "~/routes/paths";
import { _ } from "~/i18n";
import { Icon } from "../layout";
@@ -38,14 +38,6 @@ import { Icon } from "../layout";
* defining the root authentication for the fisrt time, nor when the installer
* is setting the chosen product.
* */
-const EXCLUDED_FROM = [
- ROOT.login,
- PRODUCT.changeProduct,
- PRODUCT.progress,
- ROOT.installationProgress,
- ROOT.installationFinished,
- USER.rootUser.edit,
-];
const InstallConfirmationPopup = ({ onAccept, onClose }) => {
return (
@@ -90,7 +82,7 @@ const InstallButton = (
const location = useLocation();
const hasIssues = !issues.isEmpty;
- if (EXCLUDED_FROM.includes(location.pathname)) return;
+ if (SUPPORTIVE_PATHS.includes(location.pathname)) return;
const { onClickWithIssues, ...buttonProps } = props;
const open = async () => setIsOpen(true);
From 64d27a343b712fb8bf728b91cf36be212059718d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 22:52:00 +0000
Subject: [PATCH 017/147] fix(service): do not initialize the target in
add_service
---
service/lib/agama/software/manager.rb | 2 --
1 file changed, 2 deletions(-)
diff --git a/service/lib/agama/software/manager.rb b/service/lib/agama/software/manager.rb
index f76046ea7a..3312d5138f 100644
--- a/service/lib/agama/software/manager.rb
+++ b/service/lib/agama/software/manager.rb
@@ -338,8 +338,6 @@ def registration
# code is based on https://github.com/yast/yast-registration/blob/master/src/lib/registration/sw_mgmt.rb#L365
# rubocop:disable Metrics/AbcSize
def add_service(service)
- # init repos, so we are sure we operate on "/" and have GPG imported
- initialize_target
# save repositories before refreshing added services (otherwise
# pkg-bindings will treat them as removed by the service refresh and
# unload them)
From 656091ee556e2b468211d75215c0156a5865e3f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 4 Dec 2024 23:06:06 +0000
Subject: [PATCH 018/147] fix(web): temporarily disable a TS problem
---
web/src/components/product/ProductRegistrationPage.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/web/src/components/product/ProductRegistrationPage.tsx b/web/src/components/product/ProductRegistrationPage.tsx
index ca6ea4ed94..463ba123e8 100644
--- a/web/src/components/product/ProductRegistrationPage.tsx
+++ b/web/src/components/product/ProductRegistrationPage.tsx
@@ -120,6 +120,7 @@ const RegistrationFormSection = () => {
e.preventDefault();
setError(null);
setLoading(true);
+ // @ts-ignore
register({ key, email }, { onError: onRegisterError, onSettled: () => setLoading(false) });
};
From 945b00ff3757bdfc6002fcc60cfcbfa4320054f7 Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Thu, 5 Dec 2024 10:17:36 +0100
Subject: [PATCH 019/147] fix reading global credentials file
---
service/lib/agama/registration.rb | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/service/lib/agama/registration.rb b/service/lib/agama/registration.rb
index 208c8290ed..655dc96c1d 100644
--- a/service/lib/agama/registration.rb
+++ b/service/lib/agama/registration.rb
@@ -34,7 +34,9 @@ class Registration
TARGET_DIR = "/run/agama/zypp"
private_constant :TARGET_DIR
- GLOBAL_CREDENTIALS_PATH = File.join(TARGET_DIR,
+ # FIXME: it should use TARGET_DIR instead of "/", but connect failed to read it even
+ # if fs_root passed as client params. Check with SCC guys why.
+ GLOBAL_CREDENTIALS_PATH = File.join("/",
SUSE::Connect::YaST::GLOBAL_CREDENTIALS_FILE)
private_constant :GLOBAL_CREDENTIALS_PATH
From 35f303f7dc977165eaf7415bbed1996a508f48c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 5 Dec 2024 09:49:12 +0000
Subject: [PATCH 020/147] fix(web): mount registration alert only in valid
paths
Otherwise, it will trigger the useProduct hook which might not be
available yet like in the case of the login path, in which the
QueryClient has not been initialized yet.
---
web/src/components/core/Page.tsx | 26 ++++++++++++-------
.../product/ProductRegistrationAlert.tsx | 2 ++
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/web/src/components/core/Page.tsx b/web/src/components/core/Page.tsx
index 5ddf83e5fa..0e9ad30de9 100644
--- a/web/src/components/core/Page.tsx
+++ b/web/src/components/core/Page.tsx
@@ -44,8 +44,9 @@ import { ProductRegistrationAlert } from "~/components/product";
import { _ } from "~/i18n";
import textStyles from "@patternfly/react-styles/css/utilities/Text/text";
import flexStyles from "@patternfly/react-styles/css/utilities/Flex/flex";
-import { To, useNavigate } from "react-router-dom";
+import { To, useLocation, useNavigate } from "react-router-dom";
import { isEmpty, isObject } from "~/utils";
+import { REGISTRATION, SUPPORTIVE_PATHS } from "~/routes/paths";
/**
* Props accepted by Page.Section
@@ -279,14 +280,21 @@ const Submit = ({ children, ...props }: SubmitActionProps) => {
*
* @see [Patternfly Page/PageSection](https://www.patternfly.org/components/page#pagesection)
*/
-const Content = ({ children, ...pageSectionProps }: React.PropsWithChildren) => (
- <>
-
-
- {children}
-
- >
-);
+const Content = ({ children, ...pageSectionProps }: React.PropsWithChildren) => {
+ const location = useLocation();
+ const mountRegistrationAlert = ![...SUPPORTIVE_PATHS, REGISTRATION.root].includes(
+ location.pathname,
+ );
+
+ return (
+ <>
+ {mountRegistrationAlert && }
+
+ {children}
+
+ >
+ );
+};
/**
* Component for structuring an Agama page, built on top of PF/Page/PageGroup.
diff --git a/web/src/components/product/ProductRegistrationAlert.tsx b/web/src/components/product/ProductRegistrationAlert.tsx
index 1681daaf92..241ccaf6b6 100644
--- a/web/src/components/product/ProductRegistrationAlert.tsx
+++ b/web/src/components/product/ProductRegistrationAlert.tsx
@@ -35,6 +35,8 @@ export default function ProductRegistrationAlert() {
const { selectedProduct: product } = useProduct();
const registration = useRegistration();
+ // NOTE: it shouldn't be mounted in these paths, but let's prevent rendering
+ // if so just in case.
if ([...SUPPORTIVE_PATHS, REGISTRATION.root].includes(location.pathname)) return;
if (product.registration === "No" || !isEmpty(registration.key)) return;
From f385dda1e694836ee58ced5676c9b0bfcadc260b Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Thu, 5 Dec 2024 11:43:10 +0100
Subject: [PATCH 021/147] adapt tests for workaround
---
service/test/agama/registration_test.rb | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/service/test/agama/registration_test.rb b/service/test/agama/registration_test.rb
index d34255c2ba..c62e258a61 100644
--- a/service/test/agama/registration_test.rb
+++ b/service/test/agama/registration_test.rb
@@ -90,7 +90,9 @@
it "creates credentials file" do
expect(SUSE::Connect::YaST).to receive(:create_credentials_file)
- .with("test-user", "12345", "/run/agama/zypp/etc/zypp/credentials.d/SCCcredentials")
+ .with("test-user", "12345", "/etc/zypp/credentials.d/SCCcredentials")
+ # TODO: when fixing suse-connect read of fsroot
+ # .with("test-user", "12345", "/run/agama/zypp/etc/zypp/credentials.d/SCCcredentials")
subject.register("11112222", email: "test@test.com")
end
From f532b37d1c2dbb59c15d3e7b62cb811629d638a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Thu, 5 Dec 2024 12:11:36 +0000
Subject: [PATCH 022/147] fix: use registration requirement capitalization
---
rust/agama-lib/src/software/model.rs | 1 +
web/src/components/layout/Sidebar.test.tsx | 2 +-
web/src/types/registration.ts | 2 +-
web/src/types/software.ts | 2 +-
4 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/rust/agama-lib/src/software/model.rs b/rust/agama-lib/src/software/model.rs
index 1678334473..32618542f1 100644
--- a/rust/agama-lib/src/software/model.rs
+++ b/rust/agama-lib/src/software/model.rs
@@ -60,6 +60,7 @@ pub struct RegistrationInfo {
utoipa::ToSchema,
)]
#[strum(serialize_all = "camelCase")]
+#[serde(rename_all = "camelCase")]
pub enum RegistrationRequirement {
/// Product does not require registration
#[default]
diff --git a/web/src/components/layout/Sidebar.test.tsx b/web/src/components/layout/Sidebar.test.tsx
index 346bf95317..fb47f7c809 100644
--- a/web/src/components/layout/Sidebar.test.tsx
+++ b/web/src/components/layout/Sidebar.test.tsx
@@ -38,7 +38,7 @@ const tw: Product = {
const sle: Product = {
id: "sle",
name: "SLE",
- registration: "Mandatory",
+ registration: "mandatory",
};
let selectedProduct: Product;
diff --git a/web/src/types/registration.ts b/web/src/types/registration.ts
index 4e70d22974..bb31e80435 100644
--- a/web/src/types/registration.ts
+++ b/web/src/types/registration.ts
@@ -22,7 +22,7 @@
type Registration = {
/** Registration requirement (i.e., "not-required", "optional", "mandatory") */
- requirement: string;
+ requirement: "no" | "optional" | "mandatory";
/** Registration code, if any */
code?: string;
/** Registration email, if any */
diff --git a/web/src/types/software.ts b/web/src/types/software.ts
index 59f5dcaeef..f35a9a609e 100644
--- a/web/src/types/software.ts
+++ b/web/src/types/software.ts
@@ -42,7 +42,7 @@ type Product = {
/** Product icon (e.g., "default.svg") */
icon?: string;
/** If product is registrable or not */
- registration: string;
+ registration: "no" | "optional" | "mandatory";
};
type PatternsSelection = { [key: string]: SelectedBy };
From 1d6c5a933ecd806790088f1f2d669e13110864e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Thu, 5 Dec 2024 12:32:25 +0000
Subject: [PATCH 023/147] fix(web): adapt the registration alert condition
---
web/src/components/product/ProductRegistrationAlert.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/src/components/product/ProductRegistrationAlert.tsx b/web/src/components/product/ProductRegistrationAlert.tsx
index 241ccaf6b6..7a119a6ebe 100644
--- a/web/src/components/product/ProductRegistrationAlert.tsx
+++ b/web/src/components/product/ProductRegistrationAlert.tsx
@@ -38,7 +38,7 @@ export default function ProductRegistrationAlert() {
// NOTE: it shouldn't be mounted in these paths, but let's prevent rendering
// if so just in case.
if ([...SUPPORTIVE_PATHS, REGISTRATION.root].includes(location.pathname)) return;
- if (product.registration === "No" || !isEmpty(registration.key)) return;
+ if (product.registration === "no" || !isEmpty(registration.key)) return;
return (
From 0bb69a75300624bca26cc0cb26ef13b4a2d981cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 5 Dec 2024 12:40:10 +0000
Subject: [PATCH 024/147] fix(web): show registration alert in all sections
But not rendering the link to the registration section when the user is
already there.
This commit also add the missing unit tests, covering all scenarios
implemented until now.
---
web/src/components/core/Page.tsx | 6 +-
.../product/ProductRegistrationAlert.test.tsx | 134 ++++++++++++++++++
.../product/ProductRegistrationAlert.tsx | 32 ++---
3 files changed, 152 insertions(+), 20 deletions(-)
create mode 100644 web/src/components/product/ProductRegistrationAlert.test.tsx
diff --git a/web/src/components/core/Page.tsx b/web/src/components/core/Page.tsx
index 0e9ad30de9..f72d6ba171 100644
--- a/web/src/components/core/Page.tsx
+++ b/web/src/components/core/Page.tsx
@@ -46,7 +46,7 @@ import textStyles from "@patternfly/react-styles/css/utilities/Text/text";
import flexStyles from "@patternfly/react-styles/css/utilities/Flex/flex";
import { To, useLocation, useNavigate } from "react-router-dom";
import { isEmpty, isObject } from "~/utils";
-import { REGISTRATION, SUPPORTIVE_PATHS } from "~/routes/paths";
+import { SUPPORTIVE_PATHS } from "~/routes/paths";
/**
* Props accepted by Page.Section
@@ -282,9 +282,7 @@ const Submit = ({ children, ...props }: SubmitActionProps) => {
*/
const Content = ({ children, ...pageSectionProps }: React.PropsWithChildren) => {
const location = useLocation();
- const mountRegistrationAlert = ![...SUPPORTIVE_PATHS, REGISTRATION.root].includes(
- location.pathname,
- );
+ const mountRegistrationAlert = !SUPPORTIVE_PATHS.includes(location.pathname);
return (
<>
diff --git a/web/src/components/product/ProductRegistrationAlert.test.tsx b/web/src/components/product/ProductRegistrationAlert.test.tsx
new file mode 100644
index 0000000000..856e47737a
--- /dev/null
+++ b/web/src/components/product/ProductRegistrationAlert.test.tsx
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) [2024] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+import React from "react";
+import { screen } from "@testing-library/react";
+import { installerRender, mockRoutes } from "~/test-utils";
+import ProductRegistrationAlert from "./ProductRegistrationAlert";
+import { Product, RegistrationInfo } from "~/types/software";
+import { useProduct, useRegistration } from "~/queries/software";
+import { PRODUCT, REGISTRATION, ROOT, USER } from "~/routes/paths";
+
+jest.mock("~/components/core/ChangeProductLink", () => () =>
+));
+
jest.mock("~/queries/users", () => ({
...jest.requireActual("~/queries/users"),
useRootUserMutation: () => mockRootUserMutation,
From c14bccc4afceb2c849e1021d15d8ef47344d261a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 5 Dec 2024 16:53:03 +0000
Subject: [PATCH 031/147] fix(web): fix product registration values
For using the right value according to the type. Overlooked in previous
commits.
---
web/src/components/core/ChangeProductLink.test.tsx | 4 ++--
web/src/components/layout/Header.test.tsx | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/web/src/components/core/ChangeProductLink.test.tsx b/web/src/components/core/ChangeProductLink.test.tsx
index bc71c1be9b..e6fe63daee 100644
--- a/web/src/components/core/ChangeProductLink.test.tsx
+++ b/web/src/components/core/ChangeProductLink.test.tsx
@@ -32,14 +32,14 @@ const tumbleweed: Product = {
name: "openSUSE Tumbleweed",
icon: "tumbleweed.svg",
description: "Tumbleweed description...",
- registration: "No",
+ registration: "no",
};
const microos: Product = {
id: "MicroOS",
name: "openSUSE MicroOS",
icon: "MicroOS.svg",
description: "MicroOS description",
- registration: "No",
+ registration: "no",
};
let mockUseProduct: { products: Product[]; selectedProduct?: Product };
diff --git a/web/src/components/layout/Header.test.tsx b/web/src/components/layout/Header.test.tsx
index 882a11e904..bd34ec37b8 100644
--- a/web/src/components/layout/Header.test.tsx
+++ b/web/src/components/layout/Header.test.tsx
@@ -31,14 +31,14 @@ const tumbleweed: Product = {
id: "Tumbleweed",
name: "openSUSE Tumbleweed",
description: "Tumbleweed description...",
- registration: "No",
+ registration: "no",
};
const microos: Product = {
id: "MicroOS",
name: "openSUSE MicroOS",
description: "MicroOS description",
- registration: "No",
+ registration: "no",
};
let phase: InstallationPhase;
From 123a7514d1f4483a66bd19902073f466a231a1ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Fri, 13 Dec 2024 11:51:53 +0000
Subject: [PATCH 032/147] feat(rust): remove the SLES 15.6 definition
---
products.d/agama-products.spec | 1 -
products.d/sles_156.yaml | 163 ---------------------------------
2 files changed, 164 deletions(-)
delete mode 100644 products.d/sles_156.yaml
diff --git a/products.d/agama-products.spec b/products.d/agama-products.spec
index 41aba34169..10e6448cc2 100644
--- a/products.d/agama-products.spec
+++ b/products.d/agama-products.spec
@@ -52,7 +52,6 @@ Definition of openSUSE products (Tumbleweed, Leap, MicroOS and Slowroll) for the
%{_datadir}/agama/products.d/tumbleweed.yaml
%{_datadir}/agama/products.d/leap_160.yaml
%{_datadir}/agama/products.d/slowroll.yaml
-%{_datadir}/agama/products.d/sles_156.yaml
%package sle
Summary: Definition of SLE products for the Agama installer.
diff --git a/products.d/sles_156.yaml b/products.d/sles_156.yaml
deleted file mode 100644
index 48c70ca4af..0000000000
--- a/products.d/sles_156.yaml
+++ /dev/null
@@ -1,163 +0,0 @@
-id: SLES
-name: SUSE Linux Enterprise Server 15.6
-registration: "mandatory"
-version: "15-6"
-# ------------------------------------------------------------------------------
-# WARNING: When changing the product description delete the translations located
-# at the at translations/description key below to avoid using obsolete
-# translations!!
-# ------------------------------------------------------------------------------
-description: "An open, reliable, compliant, and future-proof Linux Server choice
- that ensures the enterprise's business continuity. It is the secure and
- adaptable OS for long-term supported, innovation-ready infrastructure running
- business-critical workloads on-premises, in the cloud, and at the edge."
-icon: SUSE.svg
-# Do not manually change any translations! See README.md for more details.
-translations:
- description:
- ca: Una opció de servidor de Linux oberta, fiable, compatible i a prova del
- futur que garanteix la continuïtat del negoci de l'empresa. És el sistema
- operatiu segur i adaptable per a una infraestructura amb suport a llarg
- termini i preparada per a la innovació que executa càrregues de treball
- crítiques per a l'empresa a les instal·lacions, al núvol i a l'última.
- cs:
- Otevřená, spolehlivá, kompatibilní a perspektivní volba linuxového serveru,
- která zajišťuje kontinuitu podnikání podniku. Je to bezpečný a
- přizpůsobivý operační systém pro dlouhodobě podporovanou infrastrukturu
- připravenou na inovace, na které běží kritické podnikové úlohy v lokálním
- prostředí, v cloudu i na okraji sítě.
- es:
- Una opción de servidor Linux abierta, confiable, compatible y preparada para
- el futuro que garantiza la continuidad del negocio de la empresa. Es el
- sistema operativo seguro y adaptable para una infraestructura lista para
- la innovación y con soporte a largo plazo que ejecuta cargas de trabajo
- críticas para el negocio en las instalaciones, en la nube y en el borde.
- ja:
- オープンで信頼性が高く、各種の標準にも準拠し、将来性とビジネスの継続性を支援する Linux
- サーバです。長期のサポートが提供されていることから、安全性と順応性に優れ、オンプレミスからクラウド、エッジ環境に至るまで、様々な場所で重要なビジネス処理をこなすことのできる革新性の高いインフラストラクチャです。
- pt_BR:
- Uma escolha de servidor Linux aberta, confiável, compatível e à prova do
- futuro que garante a continuidade dos negócios da empresa. É o SO seguro e
- adaptável para infraestrutura com suporte de longo prazo e pronta para
- inovação, executando cargas de trabalho críticas para os negócios no
- local, na nuvem e na borda.
- sv: Ett öppet, pålitligt, kompatibelt och framtidssäkert Linux-serverval som
- säkerställer företagets affärskontinuitet. Det är det säkra och
- anpassningsbara operativsystemet för långsiktigt stödd, innovationsfärdig
- infrastruktur som kör affärskritiska arbetsbelastningar på plats, i molnet
- och vid kanten.
- tr:
- İşletmenin iş sürekliliğini garanti eden açık, güvenilir, uyumlu ve geleceğe
- dönük bir Linux Sunucu seçeneği. Uzun vadeli desteklenen, inovasyona hazır
- altyapı için güvenli ve uyarlanabilir işletim sistemidir. Şirket içinde,
- bulutta ve uçta iş açısından kritik iş yüklerini çalıştırır.
-software:
- installation_repositories: []
- mandatory_patterns:
- - base_traditional
- optional_patterns: null # no optional pattern shared
- user_patterns: []
- mandatory_packages:
- - NetworkManager
- optional_packages: null
- base_product: SLES
-
-security:
- lsm: selinux
- available_lsms:
- selinux:
- patterns:
- - selinux
- policy: enforcing
- none:
- patterns: null
-
-storage:
- space_policy: delete
- volumes:
- - "/"
- - "swap"
- volume_templates:
- - mount_path: "/"
- filesystem: btrfs
- btrfs:
- snapshots: true
- read_only: false
- default_subvolume: "@"
- subvolumes:
- - path: home
- - path: opt
- - path: root
- - path: srv
- - path: usr/local
- # Unified var subvolume - https://lists.opensuse.org/opensuse-packaging/2017-11/msg00017.html
- - path: var
- copy_on_write: false
- # Architecture specific subvolumes
- - path: boot/grub2/arm64-efi
- archs: aarch64
- - path: boot/grub2/arm-efi
- archs: arm
- - path: boot/grub2/i386-pc
- archs: x86_64
- - path: boot/grub2/powerpc-ieee1275
- archs: ppc,!board_powernv
- - path: boot/grub2/s390x-emu
- archs: s390
- - path: boot/grub2/x86_64-efi
- archs: x86_64
- - path: boot/grub2/riscv64-efi
- archs: riscv64
- size:
- auto: true
- outline:
- required: true
- filesystems:
- - btrfs
- - ext2
- - ext3
- - ext4
- - xfs
- auto_size:
- base_min: 5 GiB
- base_max: 15 GiB
- snapshots_increment: 250%
- max_fallback_for:
- - "/home"
- snapshots_configurable: true
- - mount_path: "swap"
- filesystem: swap
- size:
- min: 1 GiB
- max: 2 GiB
- outline:
- required: false
- filesystems:
- - swap
- - mount_path: "/home"
- filesystem: xfs
- size:
- auto: false
- min: 10 GiB
- max: unlimited
- outline:
- required: false
- filesystems:
- - btrfs
- - ext2
- - ext3
- - ext4
- - xfs
- - filesystem: xfs
- size:
- auto: false
- min: 1 GiB
- outline:
- required: false
- filesystems:
- - btrfs
- - ext2
- - ext3
- - ext4
- - xfs
- - vfat
From 828771084243331cc14c592f151ffadf76936698 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Fri, 13 Dec 2024 11:52:08 +0000
Subject: [PATCH 033/147] feat(rust): enable the registration for SLE 16
---
products.d/sles_160.yaml | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/products.d/sles_160.yaml b/products.d/sles_160.yaml
index ffe9d6a731..3c095d6b84 100644
--- a/products.d/sles_160.yaml
+++ b/products.d/sles_160.yaml
@@ -1,5 +1,7 @@
-id: SLES_16.0
+id: SLES
name: SUSE Linux Enterprise Server 16.0 Alpha
+registration: "mandatory"
+version: "16-0"
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
@@ -45,18 +47,7 @@ translations:
altyapı için güvenli ve uyarlanabilir işletim sistemidir. Şirket içinde,
bulutta ve uçta iş açısından kritik iş yüklerini çalıştırır.
software:
- installation_repositories:
- # Use plain HTTP repositories, HTTPS does not work without importing the SSL
- # certificate. It is safe as the repository is GPG checked and you neeed VPN
- # to reach the internal server anyway.
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-x86_64/
- archs: x86_64
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-aarch64/
- archs: aarch64
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-ppc64le/
- archs: ppc
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-Packages-16.0-s390x/
- archs: s390
+ installation_repositories: []
mandatory_patterns:
- base_traditional
From 4ec593adceaa26018903fa9ee1129d49532c9ff4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Fri, 13 Dec 2024 12:22:25 +0000
Subject: [PATCH 034/147] fix(web): remove an unused variable
---
web/src/components/core/IssuesDrawer.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/web/src/components/core/IssuesDrawer.tsx b/web/src/components/core/IssuesDrawer.tsx
index 656e946b25..13d3ae31c2 100644
--- a/web/src/components/core/IssuesDrawer.tsx
+++ b/web/src/components/core/IssuesDrawer.tsx
@@ -68,7 +68,6 @@ const IssuesDrawer = forwardRef(({ onClose }: { onClose: () => void }, ref) => {
if (issues.length === 0) return null;
// FIXME: address this better or use the /product(s)? namespace instead of
// /registration.
- const section = scope === "product" ? "registration" : scope;
const ariaLabelId = `${scope}-issues-section`;
return (
From 3135178f8bac0103f77e48e6260c36d1ed36af14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Fri, 13 Dec 2024 12:37:58 +0000
Subject: [PATCH 035/147] feat(products): enable registration for SLES for SAP
---
products.d/sles_sap_160.yaml | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/products.d/sles_sap_160.yaml b/products.d/sles_sap_160.yaml
index 20955514ec..8113188e09 100644
--- a/products.d/sles_sap_160.yaml
+++ b/products.d/sles_sap_160.yaml
@@ -1,5 +1,7 @@
-id: SLES_SAP_16.0
+id: SLES-SAP
name: SUSE Linux Enterprise Server for SAP Applications 16.0 Alpha
+registration: "mandatory"
+version: "16-0"
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
@@ -45,18 +47,7 @@ translations:
altyapı için güvenli ve uyarlanabilir işletim sistemidir. Şirket içinde,
bulutta ve uçta iş açısından kritik iş yüklerini çalıştırır.
software:
- installation_repositories:
- # Use plain HTTP repositories, HTTPS does not work without importing the SSL
- # certificate. It is safe as the repository is GPG checked and you neeed VPN
- # to reach the internal server anyway.
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-x86_64/
- archs: x86_64
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-aarch64/
- archs: aarch64
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-ppc64le/
- archs: ppc
- - url: http://download.suse.de/ibs/SUSE:/SLFO:/Products:/SLES:/16.0:/TEST/product/repo/SLES-SAP-Packages-16.0-s390x/
- archs: s390
+ installation_repositories: []
mandatory_patterns:
- base_traditional
From 8182c1cfd81a85d2602812a0dd979cf258f3f7c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Fri, 13 Dec 2024 15:48:52 +0000
Subject: [PATCH 036/147] fix(web): make ESLint happy
---
web/src/components/core/LoginPage.test.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/src/components/core/LoginPage.test.tsx b/web/src/components/core/LoginPage.test.tsx
index 6a61d4dd7a..8256357ee8 100644
--- a/web/src/components/core/LoginPage.test.tsx
+++ b/web/src/components/core/LoginPage.test.tsx
@@ -22,7 +22,7 @@
import React from "react";
import { screen, within } from "@testing-library/react";
-import { installerRender, mockRoutes, plainRender } from "~/test-utils";
+import { installerRender, mockRoutes } from "~/test-utils";
import { LoginPage } from "~/components/core";
import { AuthErrors } from "~/context/auth";
import { PlainLayout } from "../layout";
From 225038d84d1798cd741ff80191b2ad5abb629c84 Mon Sep 17 00:00:00 2001
From: YaST Bot
Date: Sun, 22 Dec 2024 03:07:01 +0000
Subject: [PATCH 037/147] Update web translation files
Agama-weblate commit: 80bf405fd615b81da576b152908c26992848ac10
---
web/src/languages.json | 1 +
web/src/po/po.id.js | 1203 ++++++++++++++++++++++++++++++++++++++++
web/src/po/po.pt_BR.js | 19 +-
3 files changed, 1221 insertions(+), 2 deletions(-)
create mode 100644 web/src/po/po.id.js
diff --git a/web/src/languages.json b/web/src/languages.json
index 11b8afb8a3..3bb8b12376 100644
--- a/web/src/languages.json
+++ b/web/src/languages.json
@@ -5,6 +5,7 @@
"en-US": "English",
"es-ES": "Español",
"fr-FR": "Français",
+ "id-ID": "Indonesia",
"ja-JP": "日本語",
"nb-NO": "Norsk bokmål",
"pt-BR": "Português",
diff --git a/web/src/po/po.id.js b/web/src/po/po.id.js
new file mode 100644
index 0000000000..b2f56f6c0c
--- /dev/null
+++ b/web/src/po/po.id.js
@@ -0,0 +1,1203 @@
+export default {
+ "": {
+ "plural-forms": (n) => 0,
+ "language": "id"
+ },
+ " Timezone selection": [
+ " Pemilihan zona waktu"
+ ],
+ " and ": [
+ " dan "
+ ],
+ "%s disk": [
+ "%s diska"
+ ],
+ "%s is an immutable system with atomic updates. It uses a read-only Btrfs file system updated via snapshots.": [
+ "%s adalah sistem yang tidak dapat diubah dengan pembaruan atom. Ia menggunakan sistem file Btrfs yang hanya dapat dibaca yang diperbarui melalui snapshot."
+ ],
+ "%s logo": [
+ "logo %s"
+ ],
+ "%s with %d partitions": [
+ "%s dengan partisi %d"
+ ],
+ ", ": [
+ ", "
+ ],
+ "A new LVM Volume Group": [
+ "Grup Volume LVM baru"
+ ],
+ "A new volume group will be allocated in the selected disk and the file system will be created as a logical volume.": [
+ "Grup volume baru akan dialokasikan di disk yang dipilih dan sistem file akan dibuat sebagai volume logis."
+ ],
+ "A size value is required": [
+ "Diperlukan nilai ukuran"
+ ],
+ "Accept": [
+ "Terima"
+ ],
+ "Actions": [
+ "Tindakan"
+ ],
+ "Actions for connection %s": [
+ "Tindakan untuk koneksi %s"
+ ],
+ "Activate": [
+ "Mengaktifkan"
+ ],
+ "Activate disks": [
+ "Mengaktifkan disk"
+ ],
+ "Activate new disk": [
+ "Aktifkan disk baru"
+ ],
+ "Activate zFCP disk": [
+ "Mengaktifkan disk zFCP"
+ ],
+ "Activated": [
+ "Diaktifkan"
+ ],
+ "Add DNS": [
+ "Tambahkan DNS"
+ ],
+ "Add a SSH Public Key for root": [
+ "Tambahkan Kunci Publik SSH untuk root"
+ ],
+ "Add an address": [
+ "Tambahkan alamat"
+ ],
+ "Add another DNS": [
+ "Tambahkan DNS lain"
+ ],
+ "Add another address": [
+ "Tambahkan alamat lain"
+ ],
+ "Add file system": [
+ "Tambahkan sistem berkas"
+ ],
+ "Address": [
+ "Alamat"
+ ],
+ "Addresses": [
+ "Alamat"
+ ],
+ "Addresses data list": [
+ "Daftar data alamat"
+ ],
+ "All partitions will be removed and any data in the disks will be lost.": [
+ "Semua partisi akan dihapus dan semua data dalam disk akan hilang."
+ ],
+ "Already set": [
+ "Sudah ditetapkan"
+ ],
+ "An existing disk": [
+ "Disk yang ada"
+ ],
+ "At least one address must be provided for selected mode": [
+ "Setidaknya satu alamat harus disediakan untuk mode yang dipilih"
+ ],
+ "At this point you can power off the machine.": [
+ "Pada titik ini, Anda dapat mematikan mesin."
+ ],
+ "At this point you can reboot the machine to log in to the new system.": [
+ "Pada titik ini, Anda dapat menyalakan ulang mesin untuk masuk ke sistem yang baru."
+ ],
+ "Authentication by initiator": [
+ "Otentikasi oleh inisiator"
+ ],
+ "Authentication by target": [
+ "Otentikasi berdasarkan target"
+ ],
+ "Authentication failed, please try again": [
+ "Otentikasi gagal, coba lagi"
+ ],
+ "Auto": [
+ "Otomatis"
+ ],
+ "Auto LUNs Scan": [
+ "Pemindaian LUN Otomatis"
+ ],
+ "Auto-login": [
+ "Masuk otomatis"
+ ],
+ "Automatic": [
+ "Otomatis"
+ ],
+ "Automatic (DHCP)": [
+ "Otomatis (DHCP)"
+ ],
+ "Automatically calculated size according to the selected product.": [
+ "Ukuran yang dihitung secara otomatis menurut produk yang dipilih."
+ ],
+ "Available products": [
+ "Produk yang tersedia"
+ ],
+ "Back": [
+ "Kembali"
+ ],
+ "Before %s": [
+ "Sebelum %s"
+ ],
+ "Before installing, you have to make some decisions. Click on each section to review the settings.": [
+ "Sebelum menginstal, Anda harus membuat beberapa keputusan. Klik pada setiap bagian untuk meninjau pengaturan."
+ ],
+ "Before starting the installation, you need to address the following problems:": [
+ "Sebelum memulai penginstalan, Anda perlu mengatasi masalah berikut ini:"
+ ],
+ "Cancel": [
+ "Batal"
+ ],
+ "Cannot be changed in remote installation": [
+ "Tidak dapat diubah dalam instalasi jarak jauh"
+ ],
+ "Cannot connect to Agama server": [
+ "Tidak dapat terhubung ke server Agama"
+ ],
+ "Change": [
+ "Ubah"
+ ],
+ "Change boot options": [
+ "Mengubah opsi boot"
+ ],
+ "Change product": [
+ "Mengubah produk"
+ ],
+ "Change selection": [
+ "Mengubah pilihan"
+ ],
+ "Change the root password": [
+ "Ubah kata sandi root"
+ ],
+ "Channel ID": [
+ "ID saluran"
+ ],
+ "Choose a disk for placing the boot loader": [
+ "Pilih disk untuk menempatkan boot loader"
+ ],
+ "Clear": [
+ "Hapus"
+ ],
+ "Close": [
+ "Tutup"
+ ],
+ "Configuring the product, please wait ...": [
+ "Mengkonfigurasi produk, harap tunggu ..."
+ ],
+ "Confirm": [
+ "Konfirmasi"
+ ],
+ "Confirm Installation": [
+ "Konfirmasi Instalasi"
+ ],
+ "Congratulations!": [
+ "Selamat!"
+ ],
+ "Connect": [
+ "Sambungkan"
+ ],
+ "Connect to a Wi-Fi network": [
+ "Sambungkan ke jaringan Wi-Fi"
+ ],
+ "Connect to hidden network": [
+ "Sambungkan ke jaringan tersembunyi"
+ ],
+ "Connect to iSCSI targets": [
+ "Menyambungkan ke target iSCSI"
+ ],
+ "Connected": [
+ "Tersambung"
+ ],
+ "Connected (%s)": [
+ "Tersambung (%s)"
+ ],
+ "Connected to %s": [
+ "Terhubung ke %s"
+ ],
+ "Connecting": [
+ "Menyambung"
+ ],
+ "Connection actions": [
+ "Tindakan koneksi"
+ ],
+ "Continue": [
+ "Lanjutkan"
+ ],
+ "Controllers": [
+ "Kontroler"
+ ],
+ "Could not authenticate against the server, please check it.": [
+ "Tidak dapat mengautentikasi server, silakan periksa."
+ ],
+ "Could not log in. Please, make sure that the password is correct.": [
+ "Tidak dapat masuk. Harap pastikan kata sandi sudah benar."
+ ],
+ "DASD %s": [
+ "DASD %s"
+ ],
+ "DIAG": [
+ "DIAG"
+ ],
+ "DNS": [
+ "DNS"
+ ],
+ "Deactivate": [
+ "Nonaktifkan"
+ ],
+ "Deactivated": [
+ "Dinonaktifkan"
+ ],
+ "Define a user now": [
+ "Tentukan pengguna sekarang"
+ ],
+ "Delete": [
+ "Menghapus"
+ ],
+ "Delete current content": [
+ "Hapus konten saat ini"
+ ],
+ "Details": [
+ "Detail"
+ ],
+ "Device": [
+ "Perangkat"
+ ],
+ "Device selector for new LVM volume group": [
+ "Pemilih perangkat untuk grup volume LVM baru"
+ ],
+ "Device selector for target disk": [
+ "Pemilih perangkat untuk disk target"
+ ],
+ "Devices: %s": [
+ "Perangkat: %s"
+ ],
+ "Discard": [
+ "Buang"
+ ],
+ "Disconnect": [
+ "Putuskan sambungan"
+ ],
+ "Disconnected": [
+ "Terputus"
+ ],
+ "Discover": [
+ "Temukan"
+ ],
+ "Discover iSCSI Targets": [
+ "Menemukan Target iSCSI"
+ ],
+ "Discover iSCSI targets": [
+ "Menemukan target iSCSI"
+ ],
+ "Disk": [
+ "Disk"
+ ],
+ "Disks": [
+ "Disk"
+ ],
+ "Do not configure": [
+ "Jangan konfigurasikan"
+ ],
+ "Download logs": [
+ "Unduh log"
+ ],
+ "Edit": [
+ "Edit"
+ ],
+ "Edit %s": [
+ "Edit %s"
+ ],
+ "Edit connection %s": [
+ "Edit koneksi %s"
+ ],
+ "Edit file system": [
+ "Edit sistem berkas"
+ ],
+ "Edit iSCSI Initiator": [
+ "Mengedit Inisiator iSCSI"
+ ],
+ "Edit password too": [
+ "Edit kata sandi juga"
+ ],
+ "Edit the SSH Public Key for root": [
+ "Edit Kunci Publik SSH untuk root"
+ ],
+ "Enable": [
+ "Diaktifkan"
+ ],
+ "Encrypted Device": [
+ "Perangkat Terenkripsi"
+ ],
+ "Encryption": [
+ "Enkripsi"
+ ],
+ "Encryption Password": [
+ "Kata Sandi Enkripsi"
+ ],
+ "Exact size": [
+ "Ukuran yang tepat"
+ ],
+ "Exact size for the file system.": [
+ "Ukuran yang tepat untuk sistem berkas."
+ ],
+ "File system type": [
+ "Jenis sistem berkas"
+ ],
+ "Filter by description or keymap code": [
+ "Filter berdasarkan deskripsi atau kode peta kunci"
+ ],
+ "Filter by language, territory or locale code": [
+ "Memfilter berdasarkan bahasa, wilayah, atau kode lokal"
+ ],
+ "Filter by max channel": [
+ "Memfilter menurut saluran maks"
+ ],
+ "Filter by min channel": [
+ "Memfilter menurut saluran min"
+ ],
+ "Filter by pattern title or description": [
+ "Memfilter berdasarkan judul atau deskripsi pola"
+ ],
+ "Filter by territory, time zone code or UTC offset": [
+ "Memfilter berdasarkan wilayah, kode zona waktu, atau offset UTC"
+ ],
+ "Final layout": [
+ "Tata letak akhir"
+ ],
+ "Finish": [
+ "Selesai"
+ ],
+ "Finished": [
+ "Selesai"
+ ],
+ "Fixed": [
+ "Tetap"
+ ],
+ "Forget": [
+ "Lupakan"
+ ],
+ "Forget connection %s": [
+ "Lupakan koneksi %s"
+ ],
+ "Format": [
+ "Format"
+ ],
+ "Formatted": [
+ "Diformat"
+ ],
+ "Formatting DASD devices": [
+ "Memformat perangkat DASD"
+ ],
+ "Full Disk Encryption (FDE) allows to protect the information stored at the device, including data, programs, and system files.": [
+ "Full Disk Encryption (FDE) memungkinkan untuk melindungi informasi yang tersimpan di perangkat, termasuk data, program, dan file sistem."
+ ],
+ "Full name": [
+ "Nama lengkap"
+ ],
+ "Gateway": [
+ "Gateway"
+ ],
+ "Gateway can be defined only in 'Manual' mode": [
+ "Gateway hanya dapat ditentukan dalam mode 'Manual'"
+ ],
+ "GiB": [
+ "GiB"
+ ],
+ "Hide %d subvolume action": [
+ "Menyembunyikan tindakan subvolume %d"
+ ],
+ "Hide details": [
+ "Sembunyikan detail"
+ ],
+ "IP Address": [
+ "Alamat IP"
+ ],
+ "IP address": [
+ "Alamat IP"
+ ],
+ "IP addresses": [
+ "Alamat IP"
+ ],
+ "If a local media was used to run this installer, remove it before the next boot.": [
+ "Jika media lokal digunakan untuk menjalankan penginstalasi ini, hapus media tersebut sebelum boot berikutnya."
+ ],
+ "If you continue, partitions on your hard disk will be modified according to the provided installation settings.": [
+ "Jika Anda melanjutkan, partisi pada hard disk Anda akan dimodifikasi sesuai dengan pengaturan instalasi yang disediakan."
+ ],
+ "In progress": [
+ "Dalam proses"
+ ],
+ "Incorrect IP address": [
+ "Alamat IP salah"
+ ],
+ "Incorrect password": [
+ "Kata sandi salah"
+ ],
+ "Incorrect port": [
+ "Port salah"
+ ],
+ "Incorrect user name": [
+ "Nama pengguna salah"
+ ],
+ "Initiator": [
+ "Inisiator"
+ ],
+ "Initiator name": [
+ "Nama inisiator"
+ ],
+ "Install": [
+ "Instal"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group deleting all the content of the underlying devices": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru dan hapus semua konten perangkat yang mendasarinya"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group on %s deleting all its content": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru pada %s menghapus semua kontennya"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group on %s shrinking existing partitions as needed": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru pada %s menyusutkan partisi yang ada sesuai kebutuhan"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group on %s using a custom strategy to find the needed space": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru di %s menggunakan strategi khusus untuk menemukan ruang yang dibutuhkan"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group on %s without modifying existing partitions": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru di %s tanpa memodifikasi partisi yang ada"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group shrinking existing partitions at the underlying devices as needed": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru yang menyusutkan partisi yang ada di perangkat yang mendasarinya sesuai kebutuhan"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group using a custom strategy to find the needed space at the underlying devices": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru menggunakan strategi khusus untuk menemukan ruang yang dibutuhkan di perangkat yang mendasarinya"
+ ],
+ "Install in a new Logical Volume Manager (LVM) volume group without modifying the partitions at the underlying devices": [
+ "Instal di grup volume Logical Volume Manager (LVM) baru tanpa memodifikasi partisi di perangkat yang mendasarinya"
+ ],
+ "Install new system on": [
+ "Instal sistem baru pada"
+ ],
+ "Install using device %s and deleting all its content": [
+ "Menginstal menggunakan perangkat %s dan menghapus semua isinya"
+ ],
+ "Install using device %s shrinking existing partitions as needed": [
+ "Instal menggunakan perangkat %s mengecilkan partisi yang ada sesuai kebutuhan"
+ ],
+ "Install using device %s with a custom strategy to find the needed space": [
+ "Instal menggunakan perangkat %s dengan strategi khusus untuk menemukan ruang yang dibutuhkan"
+ ],
+ "Install using device %s without modifying existing partitions": [
+ "Menginstal menggunakan perangkat %s tanpa mengubah partisi yang ada"
+ ],
+ "Installation device": [
+ "Perangkat penginstalan"
+ ],
+ "Installation will configure partitions for booting at %s.": [
+ "Instalasi akan mengkonfigurasi partisi untuk booting pada %s."
+ ],
+ "Installation will configure partitions for booting at the installation disk.": [
+ "Instalasi akan mengonfigurasi partisi untuk boot pada disk instalasi."
+ ],
+ "Installation will not configure partitions for booting.": [
+ "Instalasi tidak akan mengkonfigurasi partisi untuk booting."
+ ],
+ "Installation will take %s.": [
+ "Penginstalan akan memakan waktu %s."
+ ],
+ "Installer Options": [
+ "Opsi Penginstal"
+ ],
+ "Installer options": [
+ "Opsi penginstal"
+ ],
+ "Installing the system, please wait...": [
+ "Menginstal sistem, harap tunggu..."
+ ],
+ "Interface": [
+ "Antarmuka"
+ ],
+ "Ip prefix or netmask": [
+ "Awalan IP atau netmask"
+ ],
+ "Keyboard": [
+ "Papan ketik"
+ ],
+ "Keyboard layout": [
+ "Tata letak keyboard"
+ ],
+ "Keyboard selection": [
+ "Pemilihan keyboard"
+ ],
+ "KiB": [
+ "KiB"
+ ],
+ "LUN": [
+ "LUN"
+ ],
+ "Language": [
+ "Bahasa"
+ ],
+ "Limits for the file system size. The final size will be a value between the given minimum and maximum. If no maximum is given then the file system will be as big as possible.": [
+ "Batas untuk ukuran sistem berkas. Ukuran akhir akan berupa nilai antara minimum dan maksimum yang diberikan. Jika tidak ada nilai maksimum yang diberikan, maka sistem berkas akan sebesar mungkin."
+ ],
+ "Loading data...": [
+ "Memuat data..."
+ ],
+ "Loading installation environment, please wait.": [
+ "Memuat lingkungan penginstalan, harap tunggu."
+ ],
+ "Locale selection": [
+ "Pemilihan lokasi"
+ ],
+ "Localization": [
+ "Pelokalan"
+ ],
+ "Log in": [
+ "Masuk"
+ ],
+ "Log in as %s": [
+ "Masuk sebagai %s"
+ ],
+ "Login": [
+ "Masuk"
+ ],
+ "Login %s": [
+ "Login %s"
+ ],
+ "Login form": [
+ "Formulir masuk"
+ ],
+ "Logout": [
+ "Keluar"
+ ],
+ "Main navigation": [
+ "Navigasi utama"
+ ],
+ "Make sure you provide the correct values": [
+ "Pastikan Anda memberikan nilai yang benar"
+ ],
+ "Manage and format": [
+ "Mengelola dan memformat"
+ ],
+ "Manual": [
+ "Manual"
+ ],
+ "Maximum": [
+ "Maksimum"
+ ],
+ "Maximum desired size": [
+ "Ukuran maksimum yang diinginkan"
+ ],
+ "Maximum must be greater than minimum": [
+ "Maksimum harus lebih besar dari minimum"
+ ],
+ "Members: %s": [
+ "Anggota: %s"
+ ],
+ "Method": [
+ "Metode"
+ ],
+ "MiB": [
+ "MiB"
+ ],
+ "Minimum": [
+ "Minimum"
+ ],
+ "Minimum desired size": [
+ "Ukuran minimum yang diinginkan"
+ ],
+ "Minimum size is required": [
+ "Diperlukan ukuran minimum"
+ ],
+ "Mode": [
+ "Mode"
+ ],
+ "Modify": [
+ "Ubah"
+ ],
+ "More info for file system types": [
+ "Info lebih lanjut untuk jenis sistem berkas"
+ ],
+ "Mount Point": [
+ "Mount Point"
+ ],
+ "Mount point": [
+ "Titik pemasangan"
+ ],
+ "Multipath": [
+ "Multipath"
+ ],
+ "Name": [
+ "Nama"
+ ],
+ "Network": [
+ "Jaringan"
+ ],
+ "New": [
+ "Baru"
+ ],
+ "No": [
+ "Tidak"
+ ],
+ "No Wi-Fi supported": [
+ "Tidak mendukung Wi-Fi"
+ ],
+ "No additional software was selected.": [
+ "Tidak ada perangkat lunak tambahan yang dipilih."
+ ],
+ "No connected yet": [
+ "Belum terhubung"
+ ],
+ "No content found": [
+ "Tidak ada konten yang ditemukan"
+ ],
+ "No device selected yet": [
+ "Belum ada perangkat yang dipilih"
+ ],
+ "No iSCSI targets found.": [
+ "Tidak ditemukan target iSCSI."
+ ],
+ "No partitions will be automatically configured for booting. Use with caution.": [
+ "Tidak ada partisi yang akan dikonfigurasi secara otomatis untuk booting. Gunakan dengan hati-hati."
+ ],
+ "No root authentication method defined yet.": [
+ "Belum ada metode autentikasi root yang ditetapkan."
+ ],
+ "No user defined yet.": [
+ "Belum ada pengguna yang ditetapkan."
+ ],
+ "No visible Wi-Fi networks found": [
+ "Tidak ditemukan jaringan Wi-Fi"
+ ],
+ "No wired connections found": [
+ "Tidak ditemukan koneksi kabel"
+ ],
+ "No zFCP controllers found.": [
+ "Tidak ditemukan pengontrol zFCP."
+ ],
+ "No zFCP disks found.": [
+ "Tidak ditemukan disk zFCP."
+ ],
+ "None": [
+ "Tidak ada"
+ ],
+ "None of the keymaps match the filter.": [
+ "Tidak ada keymap yang cocok dengan filter."
+ ],
+ "None of the locales match the filter.": [
+ "Tidak ada lokasi yang cocok dengan filter."
+ ],
+ "None of the patterns match the filter.": [
+ "Tidak ada pola yang cocok dengan filter."
+ ],
+ "None of the time zones match the filter.": [
+ "Tidak ada zona waktu yang cocok dengan filter."
+ ],
+ "Not possible with the current setup. Click to know more.": [
+ "Tidak memungkinkan dengan pengaturan saat ini. Klik untuk mengetahui lebih lanjut."
+ ],
+ "Not selected yet": [
+ "Belum dipilih"
+ ],
+ "Not set": [
+ "Belum ditetapkan"
+ ],
+ "Offline devices must be activated before formatting them. Please, unselect or activate the devices listed below and try it again": [
+ "Perangkat offline harus diaktifkan sebelum memformatnya. Batalkan pilihan atau aktifkan perangkat yang tercantum di bawah ini dan coba lagi"
+ ],
+ "Offload card": [
+ "Bongkar kartu"
+ ],
+ "On boot": [
+ "Saat boot"
+ ],
+ "Only available if authentication by target is provided": [
+ "Hanya tersedia jika autentikasi berdasarkan target disediakan"
+ ],
+ "Options toggle": [
+ "Sakelar opsi"
+ ],
+ "Other": [
+ "Lainnya"
+ ],
+ "Overview": [
+ "Ikhtisar"
+ ],
+ "Partition Info": [
+ "Info Partisi"
+ ],
+ "Partitions to boot will be allocated at the following device.": [
+ "Partisi untuk boot akan dialokasikan pada perangkat berikut."
+ ],
+ "Partitions to boot will be allocated at the installation disk (%s).": [
+ "Partisi untuk boot akan dialokasikan pada disk instalasi (%s)."
+ ],
+ "Partitions to boot will be allocated at the installation disk.": [
+ "Partisi untuk boot akan dialokasikan pada disk instalasi."
+ ],
+ "Password": [
+ "Kata sandi"
+ ],
+ "Password Required": [
+ "Diperlukan Kata Sandi"
+ ],
+ "Password confirmation": [
+ "Konfirmasi kata sandi"
+ ],
+ "Password input": [
+ "Masukan kata sandi"
+ ],
+ "Password visibility button": [
+ "Tombol visibilitas kata sandi"
+ ],
+ "Passwords do not match": [
+ "Kata sandi tidak cocok"
+ ],
+ "Pending": [
+ "Tertunda"
+ ],
+ "Perform an action": [
+ "Melakukan tindakan"
+ ],
+ "PiB": [
+ "PiB"
+ ],
+ "Planned Actions": [
+ "Tindakan yang Direncanakan"
+ ],
+ "Please, be aware that a user must be defined before installing the system to be able to log into it.": [
+ "Perlu diketahui bahwa pengguna harus ditetapkan sebelum menginstal sistem agar dapat masuk ke dalamnya."
+ ],
+ "Please, cancel and check the settings if you are unsure.": [
+ "Mohon batalkan dan periksa pengaturan jika Anda tidak yakin."
+ ],
+ "Please, check whether it is running.": [
+ "Silakan periksa apakah sudah berjalan."
+ ],
+ "Please, define at least one authentication method for logging into the system as root.": [
+ "Tentukan setidaknya satu metode autentikasi untuk masuk ke sistem sebagai root."
+ ],
+ "Please, perform an iSCSI discovery in order to find available iSCSI targets.": [
+ "Lakukan penemuan iSCSI untuk menemukan target iSCSI yang tersedia."
+ ],
+ "Please, provide its password to log in to the system.": [
+ "Harap berikan kata sandi untuk masuk ke sistem."
+ ],
+ "Please, review provided settings and try again.": [
+ "Harap tinjau pengaturan yang tersedia dan coba lagi."
+ ],
+ "Please, try to activate a zFCP controller.": [
+ "Silakan coba aktifkan pengontrol zFCP."
+ ],
+ "Please, try to activate a zFCP disk.": [
+ "Silakan coba aktifkan disk zFCP."
+ ],
+ "Port": [
+ "Port"
+ ],
+ "Portal": [
+ "Portal"
+ ],
+ "Pre-installation checks": [
+ "Pemeriksaan pra-instalasi"
+ ],
+ "Prefix length or netmask": [
+ "Panjang awalan atau netmask"
+ ],
+ "Prepare more devices by configuring advanced": [
+ "Siapkan lebih banyak perangkat dengan menyiapkan konfigurasi lanjutan"
+ ],
+ "Presence of other volumes (%s)": [
+ "Keberadaan volume lain (%s)"
+ ],
+ "Protection for the information stored at the device, including data, programs, and system files.": [
+ "Perlindungan untuk informasi yang tersimpan di perangkat, termasuk data, program, dan file sistem."
+ ],
+ "Provide a password to ensure administrative access to the system.": [
+ "Berikan kata sandi untuk memastikan akses administratif ke sistem."
+ ],
+ "Question": [
+ "Pertanyaan"
+ ],
+ "Range": [
+ "Rentang"
+ ],
+ "Read zFCP devices": [
+ "Baca perangkat zFCP"
+ ],
+ "Reboot": [
+ "Maut ulang"
+ ],
+ "Reload": [
+ "Muat ulang"
+ ],
+ "Remove": [
+ "Menghapus"
+ ],
+ "Remove max channel filter": [
+ "Menghapus filter saluran maks"
+ ],
+ "Remove min channel filter": [
+ "Menghapus filter saluran min"
+ ],
+ "Reset to defaults": [
+ "Mengatur ulang ke default"
+ ],
+ "Reused %s": [
+ "Digunakan kembali %s"
+ ],
+ "Root SSH public key": [
+ "Kunci publik SSH root"
+ ],
+ "Root authentication": [
+ "Autentikasi root"
+ ],
+ "Root password": [
+ "Kata sandi root"
+ ],
+ "SD Card": [
+ "Kartu SD"
+ ],
+ "SSH Key": [
+ "Kunci SSH"
+ ],
+ "SSID": [
+ "SSID"
+ ],
+ "Search": [
+ "Cari"
+ ],
+ "Security": [
+ "Keamanan"
+ ],
+ "See more details": [
+ "Lihat detail lebih lanjut"
+ ],
+ "Select": [
+ "Pilih"
+ ],
+ "Select a disk": [
+ "Pilih disk"
+ ],
+ "Select a product": [
+ "Pilih produk"
+ ],
+ "Select booting partition": [
+ "Pilih partisi booting"
+ ],
+ "Select installation device": [
+ "Pilih perangkat instalasi"
+ ],
+ "Selected patterns": [
+ "Pola yang dipilih"
+ ],
+ "Separate LVM at %s": [
+ "Pisahkan LVM pada %s"
+ ],
+ "Server IP": [
+ "IP Server"
+ ],
+ "Set": [
+ "Tetapkan"
+ ],
+ "Set DIAG Off": [
+ "Mengatur DIAG Tidak Aktif"
+ ],
+ "Set DIAG On": [
+ "Mengatur DIAG Aktif"
+ ],
+ "Set a password": [
+ "Tetapkan kata sandi"
+ ],
+ "Set a root password": [
+ "Atur kata sandi root"
+ ],
+ "Set root SSH public key": [
+ "Atur kunci publik SSH root"
+ ],
+ "Show %d subvolume action": [
+ "Tampilkan tindakan subvolume %d"
+ ],
+ "Shrink existing partitions": [
+ "Perkecil partisi yang ada"
+ ],
+ "Size": [
+ "Ukuran"
+ ],
+ "Size unit": [
+ "Satuan ukuran"
+ ],
+ "Software": [
+ "Perangkat lunak"
+ ],
+ "Software %s": [
+ "Perangkat Lunak %s"
+ ],
+ "Software selection": [
+ "Pemilihan perangkat lunak"
+ ],
+ "Something went wrong": [
+ "Ada yang tidak beres"
+ ],
+ "Startup": [
+ "Startup"
+ ],
+ "Status": [
+ "Status"
+ ],
+ "Storage": [
+ "Penyimpanan"
+ ],
+ "Storage proposal not possible": [
+ "Proposal penyimpanan tidak memungkinkan"
+ ],
+ "Structure of the new system, including any additional partition needed for booting": [
+ "Struktur sistem baru, termasuk partisi tambahan yang diperlukan untuk booting"
+ ],
+ "TPM sealing requires the new system to be booted directly.": [
+ "Penyegelan TPM mengharuskan sistem baru untuk di-boot secara langsung."
+ ],
+ "Table with mount points": [
+ "Tabel dengan titik pemasangan"
+ ],
+ "Take your time to check your configuration before starting the installation process.": [
+ "Gunakan waktu Anda untuk memeriksa konfigurasi Anda sebelum memulai proses instalasi."
+ ],
+ "Target Password": [
+ "Kata sandi target"
+ ],
+ "Targets": [
+ "Target"
+ ],
+ "The amount of RAM in the system": [
+ "Jumlah RAM dalam sistem"
+ ],
+ "The configuration of snapshots": [
+ "Konfigurasi snapshot"
+ ],
+ "The content may be deleted": [
+ "Konten mungkin akan dihapus"
+ ],
+ "The current file system on the selected device will be mounted without formatting the device.": [
+ "Sistem file saat ini pada perangkat yang dipilih akan dipasang tanpa memformat perangkat."
+ ],
+ "The device cannot be shrunk:": [
+ "Perangkat tidak dapat dikecilkan:"
+ ],
+ "The encryption password did not work": [
+ "Kata sandi enkripsi tidak berfungsi"
+ ],
+ "The file systems are allocated at the installation device by default. Indicate a custom location to create the file system at a specific device.": [
+ "Sistem file dialokasikan pada perangkat instalasi secara default. Tunjukkan lokasi khusus untuk membuat sistem file di perangkat tertentu."
+ ],
+ "The file systems will be allocated by default as [logical volumes of a new LVM Volume Group]. The corresponding physical volumes will be created on demand as new partitions at the selected devices.": [
+ "Sistem file akan dialokasikan secara default sebagai [volume logis dari Grup Volume LVM baru]. Volume fisik yang sesuai akan dibuat sesuai permintaan sebagai partisi baru pada perangkat yang dipilih."
+ ],
+ "The file systems will be allocated by default as [new partitions in the selected device].": [
+ "Sistem file akan dialokasikan secara default sebagai [partisi baru di perangkat yang dipilih]."
+ ],
+ "The final size depends on %s.": [
+ "Ukuran akhir tergantung pada %s."
+ ],
+ "The final step to configure the Trusted Platform Module (TPM) to automatically open encrypted devices will take place during the first boot of the new system. For that to work, the machine needs to boot directly to the new boot loader.": [
+ "Langkah terakhir untuk mengonfigurasi Trusted Platform Module (TPM) agar dapat membuka perangkat yang dienkripsi secara otomatis selama booting pertama dari sistem yang baru. Agar dapat berfungsi, mesin harus melakukan booting secara langsung ke boot loader yang baru."
+ ],
+ "The following software patterns are selected for installation:": [
+ "Pola perangkat lunak berikut ini dipilih untuk instalasi:"
+ ],
+ "The installation on your machine is complete.": [
+ "Penginstalan pada mesin Anda sudah selesai."
+ ],
+ "The installation will take": [
+ "Instalasi akan memakan waktu"
+ ],
+ "The installation will take %s including:": [
+ "Instalasi akan memakan waktu %s termasuk:"
+ ],
+ "The installer requires [root] user privileges.": [
+ "Penginstal memerlukan hak akses pengguna [root]."
+ ],
+ "The options for the file system type depends on the product and the mount point.": [
+ "Opsi untuk jenis sistem berkas tergantung pada produk dan titik mount."
+ ],
+ "The password will not be needed to boot and access the data if the TPM can verify the integrity of the system. TPM sealing requires the new system to be booted directly on its first run.": [
+ "Kata sandi tidak akan diperlukan untuk mem-boot dan mengakses data jika TPM dapat memverifikasi integritas sistem. Penyegelan TPM mengharuskan sistem baru untuk di-boot secara langsung saat pertama kali dijalankan."
+ ],
+ "The system does not support Wi-Fi connections, probably because of missing or disabled hardware.": [
+ "Sistem tidak mendukung koneksi Wi-Fi, mungkin karena perangkat keras yang tidak ada atau dinonaktifkan."
+ ],
+ "The system has not been configured for connecting to a Wi-Fi network yet.": [
+ "Sistem belum dikonfigurasi untuk menghubungkan ke jaringan Wi-Fi."
+ ],
+ "The system will use %s as its default language.": [
+ "Sistem akan menggunakan %s sebagai bahasa default."
+ ],
+ "The systems will be configured as displayed below.": [
+ "Sistem akan dikonfigurasikan seperti yang ditampilkan di bawah ini."
+ ],
+ "The zFCP disk was not activated.": [
+ "Disk zFCP tidak diaktifkan."
+ ],
+ "These are the most relevant installation settings. Feel free to browse the sections in the menu for further details.": [
+ "Berikut ini adalah pengaturan instalasi yang paling relevan. Silakan telusuri bagian dalam menu untuk rincian lebih lanjut."
+ ],
+ "These limits are affected by:": [
+ "Batasan ini dipengaruhi oleh:"
+ ],
+ "This action could destroy any data stored on the devices listed below. Please, confirm that you really want to continue.": [
+ "Tindakan ini dapat menghapus data yang tersimpan pada perangkat yang tercantum di bawah ini. Harap konfirmasikan bahwa Anda benar-benar ingin melanjutkan."
+ ],
+ "This product does not allow to select software patterns during installation. However, you can add additional software once the installation is finished.": [
+ "Produk ini tidak memungkinkan untuk memilih pola perangkat lunak selama instalasi. Namun demikian, Anda dapat menambahkan perangkat lunak tambahan setelah penginstalan selesai."
+ ],
+ "This space includes the base system and the selected software patterns, if any.": [
+ "Ruang ini mencakup sistem dasar dan pola perangkat lunak yang dipilih, jika ada."
+ ],
+ "TiB": [
+ "TiB"
+ ],
+ "Time zone": [
+ "Zona waktu"
+ ],
+ "To ensure the new system is able to boot, the installer may need to create or configure some partitions in the appropriate disk.": [
+ "Untuk memastikan sistem baru dapat melakukan booting, penginstal mungkin perlu membuat atau mengonfigurasi beberapa partisi di disk yang sesuai."
+ ],
+ "Transactional root file system": [
+ "Sistem file root transaksional"
+ ],
+ "Type": [
+ "Jenis"
+ ],
+ "Unit for the maximum size": [
+ "Satuan untuk ukuran maksimum"
+ ],
+ "Unit for the minimum size": [
+ "Satuan untuk ukuran minimum"
+ ],
+ "Unselect": [
+ "Batalkan pilihan"
+ ],
+ "Unused space": [
+ "Ruang tidak terpakai"
+ ],
+ "Up to %s can be recovered by shrinking the device.": [
+ "Hingga %s dapat dipulihkan dengan mengecilkan perangkat."
+ ],
+ "Upload": [
+ "Unggah"
+ ],
+ "Upload a SSH Public Key": [
+ "Unggah Kunci Publik SSH"
+ ],
+ "Upload, paste, or drop an SSH public key": [
+ "Unggah, tempel, atau jatuhkan kunci publik SSH"
+ ],
+ "Usage": [
+ "Penggunaan"
+ ],
+ "Use available space": [
+ "Gunakan ruang yang tersedia"
+ ],
+ "Use suggested username": [
+ "Gunakan nama pengguna yang disarankan"
+ ],
+ "Use the Trusted Platform Module (TPM) to decrypt automatically on each boot": [
+ "Gunakan Modul Platform Tepercaya (TPM) untuk mendekripsi secara otomatis pada setiap boot"
+ ],
+ "User full name": [
+ "Nama lengkap pengguna"
+ ],
+ "User name": [
+ "Nama pengguna"
+ ],
+ "Username": [
+ "Nama pengguna"
+ ],
+ "Username suggestion dropdown": [
+ "Tarik-turun saran nama pengguna"
+ ],
+ "Users": [
+ "Pengguna"
+ ],
+ "Visible Wi-Fi networks": [
+ "Jaringan Wi-Fi yang terlihat"
+ ],
+ "WPA & WPA2 Personal": [
+ "WPA & WPA2 Pribadi"
+ ],
+ "WPA Password": [
+ "Kata Sandi WPA"
+ ],
+ "WWPN": [
+ "WWPN"
+ ],
+ "Waiting": [
+ "Menunggu"
+ ],
+ "Wi-Fi": [
+ "Wi-Fi"
+ ],
+ "WiFi connection form": [
+ "Formulir koneksi WiFi"
+ ],
+ "Wired": [
+ "Kabel"
+ ],
+ "Wires: %s": [
+ "Kabel: %s"
+ ],
+ "Yes": [
+ "Ya"
+ ],
+ "ZFCP": [
+ "ZFCP"
+ ],
+ "auto": [
+ "otomatis"
+ ],
+ "auto selected": [
+ "otomatis dipilih"
+ ],
+ "configured": [
+ "dikonfigurasi"
+ ],
+ "disabled": [
+ "dinonaktifkan"
+ ],
+ "enabled": [
+ "diaktifkan"
+ ],
+ "iBFT": [
+ "iBFT"
+ ],
+ "iSCSI": [
+ "iSCSI"
+ ],
+ "storage techs": [
+ "teknisi penyimpanan"
+ ],
+ "the amount of RAM in the system": [
+ "jumlah RAM dalam sistem"
+ ],
+ "the configuration of snapshots": [
+ "konfigurasi snapshot"
+ ],
+ "the presence of the file system for %s": [
+ "keberadaan sistem berkas untuk %s"
+ ],
+ "user autologin": [
+ "autologin pengguna"
+ ],
+ "using TPM unlocking": [
+ "menggunakan pembukaan kunci TPM"
+ ],
+ "without modifying any partition": [
+ "tanpa memodifikasi partisi apa pun"
+ ],
+ "zFCP": [
+ "zFCP"
+ ],
+ "zFCP Disk Activation": [
+ "Aktivasi Disk zFCP"
+ ],
+ "zFCP Disk activation form": [
+ "Formulir aktivasi Disk zFCP"
+ ]
+};
diff --git a/web/src/po/po.pt_BR.js b/web/src/po/po.pt_BR.js
index 87cae8e3c0..cfc2179cf9 100644
--- a/web/src/po/po.pt_BR.js
+++ b/web/src/po/po.pt_BR.js
@@ -188,7 +188,7 @@ export default {
"Antes %s"
],
"Before installing, you have to make some decisions. Click on each section to review the settings.": [
- ""
+ "Antes de instalar, você precisa tomar algumas decisões. Clique em cada seção para revisar as configurações."
],
"Before starting the installation, you need to address the following problems:": [
"Antes de iniciar a instalação, você precisa resolver os seguintes problemas:"
@@ -850,7 +850,7 @@ export default {
"Nenhum dos fusos horários corresponde ao filtro."
],
"Not possible with the current setup. Click to know more.": [
- ""
+ "Não é possível com a configuração atual. Clique para saber mais."
],
"Not selected yet": [
"Ainda não selecionado"
@@ -909,6 +909,9 @@ export default {
"Password confirmation": [
"Confirmação de senha"
],
+ "Password for root user": [
+ "Senha para usuário root"
+ ],
"Password input": [
"Entrada de senha"
],
@@ -963,6 +966,9 @@ export default {
"Portal": [
"Portal"
],
+ "Pre-installation checks": [
+ "Verificações de pré-instalação"
+ ],
"Prefix length or netmask": [
"Comprimento do prefixo ou máscara de rede"
],
@@ -975,6 +981,9 @@ export default {
"Protection for the information stored at the device, including data, programs, and system files.": [
"Proteção para as informações armazenadas no dispositivo, incluindo dados, programas e arquivos de sistema."
],
+ "Provide a password to ensure administrative access to the system.": [
+ "Forneça uma senha para garantir acesso administrativo ao sistema."
+ ],
"Question": [
"Pergunta"
],
@@ -1089,6 +1098,9 @@ export default {
"Set root SSH public key": [
"Definir chave pública SSH raiz"
],
+ "Setup root user authentication": [
+ "Configurar autenticação do usuário root"
+ ],
"Show %d subvolume action": [
"Mostrar ação do subvolume %d",
"Mostrar %d ações de subvolume"
@@ -1402,6 +1414,9 @@ export default {
"Yes": [
"Sim"
],
+ "You can change it or select another authentication method in the 'Users' section before installing.": [
+ "Você pode alterá-lo ou selecionar outro método de autenticação na seção \"Usuários\" antes de instalar."
+ ],
"ZFCP": [
"ZFCP"
],
From a73b4ddac224da70f541e9559719283c903388bd Mon Sep 17 00:00:00 2001
From: YaST Bot
Date: Sun, 22 Dec 2024 03:07:15 +0000
Subject: [PATCH 038/147] Update service PO files
Agama-weblate commit: 80bf405fd615b81da576b152908c26992848ac10
---
service/po/ca.po | 48 ++++++++++++++++++-------------------
service/po/cs.po | 48 ++++++++++++++++++-------------------
service/po/de.po | 52 ++++++++++++++++++++--------------------
service/po/es.po | 56 +++++++++++++++++++++----------------------
service/po/fr.po | 48 ++++++++++++++++++-------------------
service/po/id.po | 48 ++++++++++++++++++-------------------
service/po/ja.po | 48 ++++++++++++++++++-------------------
service/po/ka.po | 48 ++++++++++++++++++-------------------
service/po/nb_NO.po | 48 ++++++++++++++++++-------------------
service/po/pt_BR.po | 48 ++++++++++++++++++-------------------
service/po/ru.po | 48 ++++++++++++++++++-------------------
service/po/sv.po | 48 ++++++++++++++++++-------------------
service/po/tr.po | 48 ++++++++++++++++++-------------------
service/po/zh_Hans.po | 48 ++++++++++++++++++-------------------
14 files changed, 342 insertions(+), 342 deletions(-)
diff --git a/service/po/ca.po b/service/po/ca.po
index 681ec7c81c..8f7cc64cc5 100644
--- a/service/po/ca.po
+++ b/service/po/ca.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2024-10-30 13:48+0000\n"
"Last-Translator: David Medina \n"
"Language-Team: Catalan \n"
"Language-Team: Czech =2 && n<=4) ? 1 : 2;\n"
"X-Generator: Weblate 5.8.3\n"
-#. Runs the startup phase
-#: service/lib/agama/manager.rb:91
-msgid "Load software translations"
-msgstr "Načíst překlady softwaru"
-
-#: service/lib/agama/manager.rb:92
-msgid "Load storage translations"
-msgstr "Načíst překlady paměti"
-
#. Runs the config phase
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
msgstr "Analyzovat disky"
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Configure software"
msgstr "Konfigurovat software"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
-#: service/lib/agama/manager.rb:127
+#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
msgstr "Připravit disky"
-#: service/lib/agama/manager.rb:128
+#: service/lib/agama/manager.rb:117
msgid "Install software"
msgstr "Instalovat software"
-#: service/lib/agama/manager.rb:129
+#: service/lib/agama/manager.rb:118
msgid "Configure the system"
msgstr "Konfigurovat systém"
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr "Načíst překlady softwaru"
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr "Načíst překlady paměti"
+
#. Callback to handle unsigned files
#.
#. @param filename [String] File name
@@ -236,37 +236,37 @@ msgid "Shrinking is not supported by this device"
msgstr "Toto zařízení nepodporuje zmenšování"
#. Probes storage devices and performs an initial proposal
-#: service/lib/agama/storage/manager.rb:115
+#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
msgstr "Aktivuji úložná zařízení"
-#: service/lib/agama/storage/manager.rb:116
+#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
msgstr "Sonduji úložná zařízení"
-#: service/lib/agama/storage/manager.rb:117
+#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
msgstr "Vypočítávání návrhu úložiště"
-#: service/lib/agama/storage/manager.rb:118
+#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
msgstr "Vybírám bezpečnostní moduly Linuxu"
#. Prepares the partitioning to install the system
-#: service/lib/agama/storage/manager.rb:126
+#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
msgstr "Připravuji návrh boot zavaděče"
-#. first make bootloader proposal to be sure that required packages are installed
-#: service/lib/agama/storage/manager.rb:131
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
msgstr "Přidávám balíčky související s úložištěm"
-#: service/lib/agama/storage/manager.rb:132
+#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
msgstr "Připravuji úložná zařízení"
-#: service/lib/agama/storage/manager.rb:133
+#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
msgstr "Zapisuji konfiguraci boot zavaděče v sysconfig"
diff --git a/service/po/de.po b/service/po/de.po
index 970aa0a5ab..c56eb61b62 100644
--- a/service/po/de.po
+++ b/service/po/de.po
@@ -7,11 +7,11 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2024-12-12 06:48+0000\n"
"Last-Translator: Ettore Atalan \n"
-"Language-Team: German \n"
+"Language-Team: German \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -19,38 +19,38 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.8.4\n"
-#. Runs the startup phase
-#: service/lib/agama/manager.rb:91
-msgid "Load software translations"
-msgstr "Softwareübersetzungen laden"
-
-#: service/lib/agama/manager.rb:92
-msgid "Load storage translations"
-msgstr ""
-
#. Runs the config phase
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
msgstr "Festplatten analysieren"
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Configure software"
msgstr "Software konfigurieren"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
-#: service/lib/agama/manager.rb:127
+#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
msgstr "Festplatten vorbereiten"
-#: service/lib/agama/manager.rb:128
+#: service/lib/agama/manager.rb:117
msgid "Install software"
msgstr "Software installieren"
-#: service/lib/agama/manager.rb:129
+#: service/lib/agama/manager.rb:118
msgid "Configure the system"
msgstr "System konfigurieren"
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr "Softwareübersetzungen laden"
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr ""
+
#. Callback to handle unsigned files
#.
#. @param filename [String] File name
@@ -243,37 +243,37 @@ msgid "Shrinking is not supported by this device"
msgstr "Verkleinern wird von diesem Gerät nicht unterstützt"
#. Probes storage devices and performs an initial proposal
-#: service/lib/agama/storage/manager.rb:115
+#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
msgstr "Speichergeräte werden aktiviert"
-#: service/lib/agama/storage/manager.rb:116
+#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
msgstr "Speichergeräte werden untersucht"
-#: service/lib/agama/storage/manager.rb:117
+#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
msgstr "Speichervorschlag wird berechnet"
-#: service/lib/agama/storage/manager.rb:118
+#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
msgstr "Linux-Sicherheitsmodule werden ausgewählt"
#. Prepares the partitioning to install the system
-#: service/lib/agama/storage/manager.rb:126
+#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
msgstr "Bootloader-Vorschlag wird vorbereitet"
-#. first make bootloader proposal to be sure that required packages are installed
-#: service/lib/agama/storage/manager.rb:131
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
msgstr "Speicherbezogene Pakete werden hinzugefügt"
-#: service/lib/agama/storage/manager.rb:132
+#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
msgstr "Speichergeräte werden vorbereitet"
-#: service/lib/agama/storage/manager.rb:133
+#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
msgstr "Bootloader-Systemkonfiguration wird geschrieben"
diff --git a/service/po/es.po b/service/po/es.po
index 639cf64b95..fefb11966a 100644
--- a/service/po/es.po
+++ b/service/po/es.po
@@ -7,11 +7,11 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2024-12-11 16:48+0000\n"
"Last-Translator: \"Marina J. Donis\" \n"
-"Language-Team: Spanish \n"
+"Language-Team: Spanish \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -19,38 +19,38 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.8.4\n"
-#. Runs the startup phase
-#: service/lib/agama/manager.rb:91
-msgid "Load software translations"
-msgstr "Cargar traducciones de software"
-
-#: service/lib/agama/manager.rb:92
-msgid "Load storage translations"
-msgstr "Cargar traducciones de almacenamiento"
-
#. Runs the config phase
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
msgstr "Analizar discos"
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Configure software"
msgstr "Configurar software"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
-#: service/lib/agama/manager.rb:127
+#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
msgstr "Preparar discos"
-#: service/lib/agama/manager.rb:128
+#: service/lib/agama/manager.rb:117
msgid "Install software"
msgstr "Instalar software"
-#: service/lib/agama/manager.rb:129
+#: service/lib/agama/manager.rb:118
msgid "Configure the system"
msgstr "Configurar el sistema"
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr "Cargar traducciones de software"
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr "Cargar traducciones de almacenamiento"
+
#. Callback to handle unsigned files
#.
#. @param filename [String] File name
@@ -156,8 +156,8 @@ msgstr "Falta el tipo de sistema de archivos para \"%s\""
#, perl-brace-format
msgid "The file system type '%{filesystem}' is not suitable for '%{path}'"
msgstr ""
-"El tipo de sistema de archivos para \"%{filesystem}\" no es adecuado para \""
-"%{path}\""
+"El tipo de sistema de archivos para \"%{filesystem}\" no es adecuado para "
+"\"%{path}\""
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
@@ -244,37 +244,37 @@ msgid "Shrinking is not supported by this device"
msgstr "Este dispositivo no admite la reducción"
#. Probes storage devices and performs an initial proposal
-#: service/lib/agama/storage/manager.rb:115
+#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
msgstr "Activar dispositivos de almacenamiento"
-#: service/lib/agama/storage/manager.rb:116
+#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
msgstr "Probando los dispositivos de almacenamiento"
-#: service/lib/agama/storage/manager.rb:117
+#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
msgstr "Calcular la propuesta de almacenamiento"
-#: service/lib/agama/storage/manager.rb:118
+#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
msgstr "Seleccionar módulos de seguridad de Linux"
#. Prepares the partitioning to install the system
-#: service/lib/agama/storage/manager.rb:126
+#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
msgstr "Preparando la propuesta del gestor de arranque"
-#. first make bootloader proposal to be sure that required packages are installed
-#: service/lib/agama/storage/manager.rb:131
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
msgstr "Agregar paquetes relacionados con el almacenamiento"
-#: service/lib/agama/storage/manager.rb:132
+#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
msgstr "Preparando los dispositivos de almacenamiento"
-#: service/lib/agama/storage/manager.rb:133
+#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
msgstr "Escribiendo el gestor de arranque sysconfig"
diff --git a/service/po/fr.po b/service/po/fr.po
index 988764c3e6..7bc9543ee7 100644
--- a/service/po/fr.po
+++ b/service/po/fr.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2024-04-19 23:43+0000\n"
"Last-Translator: faila fail \n"
"Language-Team: French 1;\n"
"X-Generator: Weblate 4.9.1\n"
-#. Runs the startup phase
-#: service/lib/agama/manager.rb:91
-msgid "Load software translations"
-msgstr ""
-
-#: service/lib/agama/manager.rb:92
-msgid "Load storage translations"
-msgstr ""
-
#. Runs the config phase
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
msgstr ""
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
#, fuzzy
msgid "Configure software"
msgstr "Sonde les logiciels"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
-#: service/lib/agama/manager.rb:127
+#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
msgstr ""
-#: service/lib/agama/manager.rb:128
+#: service/lib/agama/manager.rb:117
#, fuzzy
msgid "Install software"
msgstr "Installation des logiciels"
-#: service/lib/agama/manager.rb:129
+#: service/lib/agama/manager.rb:118
msgid "Configure the system"
msgstr ""
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr ""
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr ""
+
#. Callback to handle unsigned files
#.
#. @param filename [String] File name
@@ -233,37 +233,37 @@ msgid "Shrinking is not supported by this device"
msgstr ""
#. Probes storage devices and performs an initial proposal
-#: service/lib/agama/storage/manager.rb:115
+#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
msgstr "Activation des périphériques de stockage"
-#: service/lib/agama/storage/manager.rb:116
+#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
msgstr "Sonde les périphériques de stockage"
-#: service/lib/agama/storage/manager.rb:117
+#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
msgstr "Calcul de la proposition de stockage"
-#: service/lib/agama/storage/manager.rb:118
+#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
msgstr "Sélection des modules de sécurité Linux"
#. Prepares the partitioning to install the system
-#: service/lib/agama/storage/manager.rb:126
+#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
msgstr "Préparation du chargeur d'amorçage envisagé"
-#. first make bootloader proposal to be sure that required packages are installed
-#: service/lib/agama/storage/manager.rb:131
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
msgstr "Ajout des paquets relatifs au stockage"
-#: service/lib/agama/storage/manager.rb:132
+#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
msgstr "Préparation des périphériques de stockage"
-#: service/lib/agama/storage/manager.rb:133
+#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
msgstr "Écriture du sysconfig du chargeur d'amorçage"
diff --git a/service/po/id.po b/service/po/id.po
index 1e03bd596d..89f49fdbd9 100644
--- a/service/po/id.po
+++ b/service/po/id.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2023-12-28 21:02+0000\n"
"Last-Translator: Arif Budiman \n"
"Language-Team: Indonesian \n"
"Language-Team: Japanese \n"
"Language-Team: Georgian \n"
"Language-Team: Norwegian Bokmål \n"
"Language-Team: Portuguese (Brazil) 1;\n"
"X-Generator: Weblate 5.8.3\n"
-#. Runs the startup phase
-#: service/lib/agama/manager.rb:91
-msgid "Load software translations"
-msgstr "Carregar traduções de software"
-
-#: service/lib/agama/manager.rb:92
-msgid "Load storage translations"
-msgstr "Traduções de armazenamento de carga"
-
#. Runs the config phase
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
msgstr "Analizar discos"
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Configure software"
msgstr "Configurar software"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
-#: service/lib/agama/manager.rb:127
+#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
msgstr "Preparar discos"
-#: service/lib/agama/manager.rb:128
+#: service/lib/agama/manager.rb:117
msgid "Install software"
msgstr "Instalar software"
-#: service/lib/agama/manager.rb:129
+#: service/lib/agama/manager.rb:118
msgid "Configure the system"
msgstr "Configurar o sistema"
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr "Carregar traduções de software"
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr "Traduções de armazenamento de carga"
+
#. Callback to handle unsigned files
#.
#. @param filename [String] File name
@@ -241,37 +241,37 @@ msgid "Shrinking is not supported by this device"
msgstr "O encolhimento não é suportado por este dispositivo"
#. Probes storage devices and performs an initial proposal
-#: service/lib/agama/storage/manager.rb:115
+#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
msgstr "Ativando dispositivos de armazenamento"
-#: service/lib/agama/storage/manager.rb:116
+#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
msgstr "Sondando dispositivos de armazenamento"
-#: service/lib/agama/storage/manager.rb:117
+#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
msgstr "Calculando a proposta de armazenamento"
-#: service/lib/agama/storage/manager.rb:118
+#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
msgstr "Selecionando módulos de segurança do Linux"
#. Prepares the partitioning to install the system
-#: service/lib/agama/storage/manager.rb:126
+#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
msgstr "Preparando proposta de bootloader"
-#. first make bootloader proposal to be sure that required packages are installed
-#: service/lib/agama/storage/manager.rb:131
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
msgstr "Adicionando pacotes relacionados ao armazenamento"
-#: service/lib/agama/storage/manager.rb:132
+#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
msgstr "Preparando os dispositivos de armazenamento"
-#: service/lib/agama/storage/manager.rb:133
+#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
msgstr "Escrevendo sysconfig do bootloader"
diff --git a/service/po/ru.po b/service/po/ru.po
index 30a34af926..358ca10b46 100644
--- a/service/po/ru.po
+++ b/service/po/ru.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2024-06-26 10:46+0000\n"
"Last-Translator: Aleksey Fedorov \n"
"Language-Team: Russian =20) ? 1 : 2;\n"
"X-Generator: Weblate 5.6\n"
-#. Runs the startup phase
-#: service/lib/agama/manager.rb:91
-msgid "Load software translations"
-msgstr ""
-
-#: service/lib/agama/manager.rb:92
-msgid "Load storage translations"
-msgstr ""
-
#. Runs the config phase
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
msgstr "Анализ дисков"
-#: service/lib/agama/manager.rb:107
+#: service/lib/agama/manager.rb:93
msgid "Configure software"
msgstr "Настройка программного обеспечения"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
-#: service/lib/agama/manager.rb:127
+#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
msgstr "Подготовка дисков"
-#: service/lib/agama/manager.rb:128
+#: service/lib/agama/manager.rb:117
msgid "Install software"
msgstr "Установка программного обеспечения"
-#: service/lib/agama/manager.rb:129
+#: service/lib/agama/manager.rb:118
msgid "Configure the system"
msgstr "Настройка системы"
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr ""
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr ""
+
#. Callback to handle unsigned files
#.
#. @param filename [String] File name
@@ -232,37 +232,37 @@ msgid "Shrinking is not supported by this device"
msgstr ""
#. Probes storage devices and performs an initial proposal
-#: service/lib/agama/storage/manager.rb:115
+#: service/lib/agama/storage/manager.rb:120
msgid "Activating storage devices"
msgstr "Активация устройств хранения"
-#: service/lib/agama/storage/manager.rb:116
+#: service/lib/agama/storage/manager.rb:121
msgid "Probing storage devices"
msgstr "Поиск устройств хранения"
-#: service/lib/agama/storage/manager.rb:117
+#: service/lib/agama/storage/manager.rb:122
msgid "Calculating the storage proposal"
msgstr "Расчет предложения по хранению"
-#: service/lib/agama/storage/manager.rb:118
+#: service/lib/agama/storage/manager.rb:123
msgid "Selecting Linux Security Modules"
msgstr "Выбор модулей безопасности Linux"
#. Prepares the partitioning to install the system
-#: service/lib/agama/storage/manager.rb:126
+#: service/lib/agama/storage/manager.rb:131
msgid "Preparing bootloader proposal"
msgstr "Подготовка предложения по загрузчику"
-#. first make bootloader proposal to be sure that required packages are installed
-#: service/lib/agama/storage/manager.rb:131
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
msgid "Adding storage-related packages"
msgstr "Добавление пакетов, связанных с хранилищем"
-#: service/lib/agama/storage/manager.rb:132
+#: service/lib/agama/storage/manager.rb:139
msgid "Preparing the storage devices"
msgstr "Подготовка устройств хранения"
-#: service/lib/agama/storage/manager.rb:133
+#: service/lib/agama/storage/manager.rb:140
msgid "Writing bootloader sysconfig"
msgstr "Запись системной конфигурации загрузчика"
diff --git a/service/po/sv.po b/service/po/sv.po
index c546697be5..27ec82f7cc 100644
--- a/service/po/sv.po
+++ b/service/po/sv.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-10 02:55+0000\n"
+"POT-Creation-Date: 2024-12-22 02:45+0000\n"
"PO-Revision-Date: 2024-10-29 12:48+0000\n"
"Last-Translator: Luna Jernberg \n"
"Language-Team: Swedish \n"
"Language-Team: Turkish \n"
"Language-Team: Chinese (Simplified)
Date: Sun, 22 Dec 2024 03:15:07 +0000
Subject: [PATCH 039/147] Update translations in the product files
Agama-weblate commit: 80bf405fd615b81da576b152908c26992848ac10
---
products.d/slowroll.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/products.d/slowroll.yaml b/products.d/slowroll.yaml
index 357fac7d80..e4d1b25d36 100644
--- a/products.d/slowroll.yaml
+++ b/products.d/slowroll.yaml
@@ -31,6 +31,10 @@ translations:
entre paquetes "estables" y más nuevos.
ja: 実験的なディストリビューションではありますが、 Tumbleweed よりは比較的ゆっくりした、かつ Leap よりは速いペースで公開される
openSUSE ローリングリリース型ディストリビューションです。 "安定性" と最新パッケージの中間を目指しています。
+ pt_BR: Uma versão experimental e um pouco mais lenta do openSUSE, projetada para
+ atualizar com menos frequência que o Tumbleweed, mas com mais frequência
+ que o Leap, sem forçar os usuários a escolher entre pacotes "estáveis" e
+ mais novos.
sv: En experimentell och något långsammare rullande utgåva av openSUSE utformad
för att få nya paketuppdateringar mer sällan än Tumbleweed men oftare än
Leap utan att tvinga användarna att välja mellan "stabila" eller nyare
From 767b8d756de05c3154d6fd283354fc4bb4225a41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 19 Dec 2024 15:19:08 +0000
Subject: [PATCH 040/147] refactor(web): migrate core/EmailInput to TypeScript
---
...mailInput.test.jsx => EmailInput.test.tsx} | 51 ++++++++++---------
.../core/{EmailInput.jsx => EmailInput.tsx} | 29 +++++------
2 files changed, 42 insertions(+), 38 deletions(-)
rename web/src/components/core/{EmailInput.test.jsx => EmailInput.test.tsx} (74%)
rename web/src/components/core/{EmailInput.jsx => EmailInput.tsx} (67%)
diff --git a/web/src/components/core/EmailInput.test.jsx b/web/src/components/core/EmailInput.test.tsx
similarity index 74%
rename from web/src/components/core/EmailInput.test.jsx
rename to web/src/components/core/EmailInput.test.tsx
index b4572709f3..1a050e9f15 100644
--- a/web/src/components/core/EmailInput.test.jsx
+++ b/web/src/components/core/EmailInput.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -23,9 +23,35 @@
import React, { useState } from "react";
import { screen } from "@testing-library/react";
-import EmailInput from "./EmailInput";
+import EmailInput, { EmailInputProps } from "./EmailInput";
import { plainRender } from "~/test-utils";
+/**
+ * Controlled component for testing the EmailInputProps
+ *
+ * Instead of testing if given callbacks are called, below tests are going to
+ * check the rendered result to be more aligned with the React Testing Library
+ * principles, https://testing-library.com/docs/guiding-principles/
+ *
+ */
+const EmailInputTest = (props: EmailInputProps) => {
+ const [email, setEmail] = useState("");
+ const [isValid, setIsValid] = useState(true);
+
+ return (
+ <>
+ setEmail(v)}
+ onValidate={setIsValid}
+ />
+ {email &&
Email value updated!
}
+ {isValid === false &&
Email is not valid!
}
+ >
+ );
+};
+
describe("EmailInput component", () => {
it("renders an email input", () => {
plainRender(
@@ -36,27 +62,6 @@ describe("EmailInput component", () => {
expect(inputField).toHaveAttribute("type", "email");
});
- // Using a controlled component for testing the rendered result instead of testing if
- // the given onChange callback is called. The former is more aligned with the
- // React Testing Library principles, https://testing-library.com/docs/guiding-principles/
- const EmailInputTest = (props) => {
- const [email, setEmail] = useState("");
- const [isValid, setIsValid] = useState(true);
-
- return (
- <>
- setEmail(v)}
- onValidate={setIsValid}
- />
- {email &&
Email value updated!
}
- {isValid === false &&
Email is not valid!
}
- >
- );
- };
-
it("triggers onChange callback", async () => {
const { user } = plainRender();
const emailInput = screen.getByRole("textbox", { name: "Test email" });
diff --git a/web/src/components/core/EmailInput.jsx b/web/src/components/core/EmailInput.tsx
similarity index 67%
rename from web/src/components/core/EmailInput.jsx
rename to web/src/components/core/EmailInput.tsx
index 6bda2447b5..5d3bf09335 100644
--- a/web/src/components/core/EmailInput.jsx
+++ b/web/src/components/core/EmailInput.tsx
@@ -21,28 +21,25 @@
*/
import React, { useEffect, useState } from "react";
-import { InputGroup, TextInput } from "@patternfly/react-core";
-import { noop } from "~/utils";
+import { InputGroup, TextInput, TextInputProps } from "@patternfly/react-core";
+import { isEmpty, noop } from "~/utils";
/**
* Email validation.
*
* Code inspired by https://github.com/manishsaraan/email-validator/blob/master/index.js
- *
- * @param {string} email
- * @returns {boolean}
*/
-const validateEmail = (email) => {
+const validateEmail = (email: string) => {
const regexp =
/^[-!#$%&'*+/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
- const validateFormat = (email) => {
+ const validateFormat = (email: string) => {
const parts = email.split("@");
return parts.length === 2 && regexp.test(email);
};
- const validateSizes = (email) => {
+ const validateSizes = (email: string) => {
const [account, address] = email.split("@");
if (account.length > 64) return false;
@@ -58,27 +55,29 @@ const validateEmail = (email) => {
return validateFormat(email) && validateSizes(email);
};
+export type EmailInputProps = TextInputProps & { onValidate?: (isValid: boolean) => void };
+
/**
* Renders an email input field which validates its value.
* @component
*
- * @param {(boolean) => void} onValidate - Callback to be called every time the input value is
+ * @param onValidate - Callback to be called every time the input value is
* validated.
- * @param {Object} props - Props matching the {@link https://www.patternfly.org/components/forms/text-input PF/TextInput},
+ * @param props - Props matching the {@link https://www.patternfly.org/components/forms/text-input PF/TextInput},
* except `type` and `validated` which are managed by the component.
*/
-export default function EmailInput({ onValidate = noop, ...props }) {
+export default function EmailInput({ onValidate = noop, value, ...props }: EmailInputProps) {
const [isValid, setIsValid] = useState(true);
useEffect(() => {
- const isValid = props.value.length === 0 || validateEmail(props.value);
+ const isValid = typeof value === "string" && (isEmpty(value) || validateEmail(value));
setIsValid(isValid);
- onValidate(isValid);
- }, [onValidate, props.value, setIsValid]);
+ typeof onValidate === "function" && onValidate(isValid);
+ }, [onValidate, value, setIsValid]);
return (
-
+
);
}
From 7a412fd0fde69b125be18996a24e06765a9c9029 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Mon, 23 Dec 2024 22:25:06 +0000
Subject: [PATCH 041/147] refactor(web): migrate src/client to TypeScript
---
web/src/client/{index.js => index.ts} | 50 ++++++++++++++---------
web/src/client/{ws.js => ws.ts} | 58 +++++++++++++--------------
2 files changed, 59 insertions(+), 49 deletions(-)
rename web/src/client/{index.js => index.ts} (55%)
rename web/src/client/{ws.js => ws.ts} (85%)
diff --git a/web/src/client/index.js b/web/src/client/index.ts
similarity index 55%
rename from web/src/client/index.js
rename to web/src/client/index.ts
index 5e161ef5f3..97826e5cd5 100644
--- a/web/src/client/index.js
+++ b/web/src/client/index.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2021-2023] SUSE LLC
+ * Copyright (c) [2021-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -20,28 +20,40 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import { WSClient } from "./ws";
-/**
- * @typedef {object} InstallerClient
- * @property {() => boolean} isConnected - determines whether the client is connected
- * @property {() => boolean} isRecoverable - determines whether the client is recoverable after disconnected
- * @property {(handler: () => void) => (() => void)} onConnect - registers a handler to run
- * @property {(handler: () => void) => (() => void)} onDisconnect - registers a handler to run
- * when the connection is lost. It returns a function to deregister the
- * handler.
- * @property {(handler: (any) => void) => (() => void)} onEvent - registers a handler to run on events
- */
+type VoidFn = () => void;
+type BooleanFn = () => boolean;
+type EventHandlerFn = (event) => void;
+
+export type InstallerClient = {
+ /** Whether the client is connected. */
+ isConnected: BooleanFn;
+ /** Whether the client is recoverable after disconnecting. */
+ isRecoverable: BooleanFn;
+ /**
+ * Registers a handler to run when connection is set. It returns a function
+ * for deregistering the handler.
+ */
+ onConnect: (handler: VoidFn) => VoidFn;
+ /**
+ * Registers a handler to run when connection is lost. It returns a function
+ * for deregistering the handler.
+ */
+ onDisconnect: (handler: VoidFn) => VoidFn;
+ /**
+ * Registers a handler to run on events. It returns a function for
+ * deregistering the handler.
+ */
+ onEvent: (handler: EventHandlerFn) => VoidFn;
+};
/**
* Creates the Agama client
*
- * @param {URL} url - URL of the HTTP API.
- * @return {InstallerClient}
+ * @param url - URL of the HTTP API.
*/
-const createClient = (url) => {
+const createClient = (url: URL): InstallerClient => {
url.hash = "";
url.pathname = url.pathname.concat("api/ws");
url.protocol = url.protocol === "http:" ? "ws" : "wss";
@@ -53,9 +65,9 @@ const createClient = (url) => {
return {
isConnected,
isRecoverable,
- onConnect: (handler) => ws.onOpen(handler),
- onDisconnect: (handler) => ws.onClose(handler),
- onEvent: (handler) => ws.onEvent(handler),
+ onConnect: (handler: VoidFn) => ws.onOpen(handler),
+ onDisconnect: (handler: VoidFn) => ws.onClose(handler),
+ onEvent: (handler: EventHandlerFn) => ws.onEvent(handler),
};
};
diff --git a/web/src/client/ws.js b/web/src/client/ws.ts
similarity index 85%
rename from web/src/client/ws.js
rename to web/src/client/ws.ts
index b63c8e2a51..608001f2cd 100644
--- a/web/src/client/ws.js
+++ b/web/src/client/ws.ts
@@ -20,19 +20,13 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
-/**
- * @callback RemoveFn
- * @return {void}
- */
+type RemoveFn = () => void;
+type BaseHandlerFn = () => void;
+type EventHandlerFn = (event) => void;
/**
* Enum for the WebSocket states.
- *
- *
*/
-
const SocketStates = Object.freeze({
CONNECTED: 0,
CONNECTING: 1,
@@ -52,10 +46,25 @@ const ATTEMPT_INTERVAL = 1000;
* HTTPClient API.
*/
class WSClient {
+ url: string;
+
+ client: WebSocket;
+
+ handlers: {
+ open: Array;
+ close: Array;
+ error: Array;
+ events: Array;
+ };
+
+ reconnectAttempts: number;
+
+ timeout: ReturnType;
+
/**
- * @param {URL} url - Websocket URL.
+ * @param url - Websocket URL.
*/
- constructor(url) {
+ constructor(url: URL) {
this.url = url.toString();
this.handlers = {
@@ -126,13 +135,10 @@ class WSClient {
/**
* Registers a handler for events.
*
- * The handler is executed for all the events. It is up to the callback to
- * filter the relevant events.
- *
- * @param {(object) => void} func - Handler function to register.
- * @return {RemoveFn}
+ * The handler is executed for all events. It is up to the callback to
+ * filter the relevant ones for it.
*/
- onEvent(func) {
+ onEvent(func: EventHandlerFn): RemoveFn {
this.handlers.events.push(func);
return () => {
const position = this.handlers.events.indexOf(func);
@@ -144,11 +150,8 @@ class WSClient {
* Registers a handler for close socket.
*
* The handler is executed when the socket is close.
- *
- * @param {(object) => void} func - Handler function to register.
- * @return {RemoveFn}
*/
- onClose(func) {
+ onClose(func: BaseHandlerFn): RemoveFn {
this.handlers.close.push(func);
return () => {
@@ -161,10 +164,8 @@ class WSClient {
* Registers a handler for open socket.
*
* The handler is executed when the socket is open.
- * @param {(object) => void} func - Handler function to register.
- * @return {RemoveFn}
*/
- onOpen(func) {
+ onOpen(func: BaseHandlerFn): RemoveFn {
this.handlers.open.push(func);
return () => {
@@ -177,11 +178,8 @@ class WSClient {
* Registers a handler for socket errors.
*
* The handler is executed when an error is reported by the socket.
- *
- * @param {(object) => void} func - Handler function to register.
- * @return {RemoveFn}
*/
- onError(func) {
+ onError(func: BaseHandlerFn): RemoveFn {
this.handlers.error.push(func);
return () => {
@@ -195,9 +193,9 @@ class WSClient {
*
* Dispatchs an event by running all the handlers.
*
- * @param {object} event - Event object, which is basically a websocket message.
+ * @param event - Event object, which is basically a websocket message.
*/
- dispatchEvent(event) {
+ dispatchEvent(event: MessageEvent) {
const eventObject = JSON.parse(event.data);
this.handlers.events.forEach((f) => f(eventObject));
}
From 77cf091b12efdeb6d627a411fefb6b5ebbd12cad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 26 Dec 2024 15:42:19 +0000
Subject: [PATCH 042/147] refactor(web): migrate contexts to TypeScript
---
web/src/context/{app.jsx => app.tsx} | 7 +--
web/src/context/{auth.jsx => auth.tsx} | 6 +--
...{installer.test.jsx => installer.test.tsx} | 4 +-
.../context/{installer.jsx => installer.tsx} | 43 ++++++++-----------
web/src/context/{root.jsx => root.tsx} | 9 +---
5 files changed, 25 insertions(+), 44 deletions(-)
rename web/src/context/{app.jsx => app.tsx} (89%)
rename web/src/context/{auth.jsx => auth.tsx} (95%)
rename web/src/context/{installer.test.jsx => installer.test.tsx} (94%)
rename web/src/context/{installer.jsx => installer.tsx} (75%)
rename web/src/context/{root.jsx => root.tsx} (84%)
diff --git a/web/src/context/app.jsx b/web/src/context/app.tsx
similarity index 89%
rename from web/src/context/app.jsx
rename to web/src/context/app.tsx
index 9ba49bf1f0..70686fde79 100644
--- a/web/src/context/app.jsx
+++ b/web/src/context/app.tsx
@@ -20,8 +20,6 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React from "react";
import { InstallerClientProvider } from "./installer";
import { InstallerL10nProvider } from "./installerL10n";
@@ -31,11 +29,8 @@ const queryClient = new QueryClient();
/**
* Combines all application providers.
- *
- * @param {object} props
- * @param {React.ReactNode} [props.children] - content to display within the provider.
*/
-function AppProviders({ children }) {
+function AppProviders({ children }: React.PropsWithChildren) {
return (
diff --git a/web/src/context/auth.jsx b/web/src/context/auth.tsx
similarity index 95%
rename from web/src/context/auth.jsx
rename to web/src/context/auth.tsx
index 536e8c054b..65154a29a7 100644
--- a/web/src/context/auth.jsx
+++ b/web/src/context/auth.tsx
@@ -20,8 +20,6 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React, { useCallback, useEffect, useState } from "react";
const AuthContext = React.createContext(null);
@@ -48,11 +46,11 @@ const AuthErrors = Object.freeze({
* @param {object} props
* @param {React.ReactNode} [props.children] - content to display within the provider
*/
-function AuthProvider({ children }) {
+function AuthProvider({ children }: React.PropsWithChildren) {
const [isLoggedIn, setIsLoggedIn] = useState(undefined);
const [error, setError] = useState(null);
- const login = useCallback(async (password) => {
+ const login = useCallback(async (password: string) => {
const response = await fetch("/api/auth", {
method: "POST",
body: JSON.stringify({ password }),
diff --git a/web/src/context/installer.test.jsx b/web/src/context/installer.test.tsx
similarity index 94%
rename from web/src/context/installer.test.jsx
rename to web/src/context/installer.test.tsx
index cd55969ffd..5c4125da1e 100644
--- a/web/src/context/installer.test.jsx
+++ b/web/src/context/installer.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -41,7 +41,7 @@ const ClientStatus = () => {
describe("installer context", () => {
beforeEach(() => {
- createDefaultClient.mockImplementation(() => {
+ (createDefaultClient as jest.Mock).mockImplementation(() => {
return {
onConnect: jest.fn(),
onDisconnect: jest.fn(),
diff --git a/web/src/context/installer.jsx b/web/src/context/installer.tsx
similarity index 75%
rename from web/src/context/installer.jsx
rename to web/src/context/installer.tsx
index b2a96aefc2..6b48662e7d 100644
--- a/web/src/context/installer.jsx
+++ b/web/src/context/installer.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2021-2023] SUSE LLC
+ * Copyright (c) [2021-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -20,10 +20,21 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React, { useState, useEffect } from "react";
-import { createDefaultClient } from "~/client";
+import { createDefaultClient, InstallerClient } from "~/client";
+
+type ClientStatus = {
+ /** Whether the client is connected or not. */
+ connected: boolean;
+ /** Whether the client present an error and cannot reconnect. */
+ error: boolean;
+};
+
+type InstallerClientProviderProps = React.PropsWithChildren<{
+ /** Client to connect to Agama service; if it is undefined, it instantiates a
+ * new one using the address registered in /run/agama/bus.address. */
+ client?: InstallerClient;
+}>;
const InstallerClientContext = React.createContext(null);
// TODO: we use a separate context to avoid changing all the codes to
@@ -35,10 +46,8 @@ const InstallerClientStatusContext = React.createContext({
/**
* Returns the D-Bus installer client
- *
- * @return {import("~/client").InstallerClient}
*/
-function useInstallerClient() {
+function useInstallerClient(): InstallerClient {
const context = React.useContext(InstallerClientContext);
if (context === undefined) {
throw new Error("useInstallerClient must be used within a InstallerClientProvider");
@@ -49,15 +58,8 @@ function useInstallerClient() {
/**
* Returns the client status.
- *
- * @typedef {object} ClientStatus
- * @property {boolean} connected - whether the client is connected
- * @property {boolean} error - whether the client present an error and cannot
- * reconnect
- *
- * @return {ClientStatus} installer client status
*/
-function useInstallerClientStatus() {
+function useInstallerClientStatus(): ClientStatus {
const context = React.useContext(InstallerClientStatusContext);
if (!context) {
throw new Error("useInstallerClientStatus must be used within a InstallerClientProvider");
@@ -66,16 +68,7 @@ function useInstallerClientStatus() {
return context;
}
-/**
- * @param {object} props
- * @param {import("~/client").InstallerClient|undefined} [props.client] client to connect to
- * Agama service; if it is undefined, it instantiates a new one using the address
- * registered in /run/agama/bus.address.
- * @param {number} [props.interval=2000] - Interval in milliseconds between connection attempt
- * (2000 by default).
- * @param {React.ReactNode} [props.children] - content to display within the provider
- */
-function InstallerClientProvider({ children, client = null }) {
+function InstallerClientProvider({ children, client = null }: InstallerClientProviderProps) {
const [value, setValue] = useState(client);
const [connected, setConnected] = useState(false);
const [error, setError] = useState(false);
diff --git a/web/src/context/root.jsx b/web/src/context/root.tsx
similarity index 84%
rename from web/src/context/root.jsx
rename to web/src/context/root.tsx
index e47d708b9f..cb1dfbd5cc 100644
--- a/web/src/context/root.jsx
+++ b/web/src/context/root.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -20,19 +20,14 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React, { Suspense } from "react";
import { AuthProvider } from "./auth";
import { Loading } from "~/components/layout";
/**
* Combines all application providers.
- *
- * @param {object} props
- * @param {React.ReactNode} [props.children] - content to display within the provider.
*/
-function RootProviders({ children }) {
+function RootProviders({ children }: React.PropsWithChildren) {
return (
}>
{children}
From ea242a3a3ce6ee5132cd433d647de18d24e36f6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 26 Dec 2024 16:32:34 +0000
Subject: [PATCH 043/147] refactor(web): migrate src/Protected to TypeScript
---
web/src/{Protected.jsx => Protected.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename web/src/{Protected.jsx => Protected.tsx} (100%)
diff --git a/web/src/Protected.jsx b/web/src/Protected.tsx
similarity index 100%
rename from web/src/Protected.jsx
rename to web/src/Protected.tsx
From 760a780fcb45ce1292ae8db62845a1d1b40108b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 26 Dec 2024 16:59:29 +0000
Subject: [PATCH 044/147] refactor(web): migrate i18n to TypeScript
---
web/src/{i18n.test.js => i18n.test.ts} | 2 +-
web/src/{i18n.js => i18n.ts} | 44 ++++++++++++--------------
2 files changed, 22 insertions(+), 24 deletions(-)
rename web/src/{i18n.test.js => i18n.test.ts} (98%)
rename web/src/{i18n.js => i18n.ts} (74%)
diff --git a/web/src/i18n.test.js b/web/src/i18n.test.ts
similarity index 98%
rename from web/src/i18n.test.js
rename to web/src/i18n.test.ts
index 483e8ae0fc..6c620a30b1 100644
--- a/web/src/i18n.test.js
+++ b/web/src/i18n.test.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/i18n.js b/web/src/i18n.ts
similarity index 74%
rename from web/src/i18n.js
rename to web/src/i18n.ts
index 93a0bed507..0ab39d6df1 100644
--- a/web/src/i18n.js
+++ b/web/src/i18n.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -30,20 +30,18 @@ import agama from "~/agama";
/**
* Tests whether a special testing language is used.
- *
- * @returns {boolean} true if the testing language is set
*/
-const isTestingLanguage = () => agama.language === "xx";
+const isTestingLanguage = (): boolean => agama.language === "xx";
/**
* "Translate" the string to special "xx" testing language.
* It just replaces all alpha characters with "x".
* It keeps the percent placeholders like "%s" or "%d" unmodified.
*
- * @param {string} str input string
- * @returns {string} "translated" string
+ * @param str input string
+ * @returns "translated" string
*/
-const xTranslate = (str) => {
+const xTranslate = (str: string): string => {
let result = "";
let wasPercent = false;
@@ -70,23 +68,23 @@ const xTranslate = (str) => {
* Returns a translated text in the current locale or the original text if the
* translation is not found.
*
- * @param {string} str the input string to translate
- * @return {string} translated or original text
+ * @param str the input string to translate
+ * @return translated or original text
*/
-const _ = (str) => (isTestingLanguage() ? xTranslate(str) : agama.gettext(str));
+const _ = (str: string): string => (isTestingLanguage() ? xTranslate(str) : agama.gettext(str));
/**
* Similar to the _() function. This variant returns singular or plural form
* depending on an additional "num" argument.
*
* @see {@link _} for further information
- * @param {string} str1 the input string in the singular form
- * @param {string} strN the input string in the plural form
- * @param {number} n the actual number which decides whether to use the
+ * @param str1 the input string in the singular form
+ * @param strN the input string in the plural form
+ * @param n the actual number which decides whether to use the
* singular or plural form
- * @return {string} translated or original text
+ * @return translated or original text
*/
-const n_ = (str1, strN, n) => {
+const n_ = (str1: string, strN: string, n: number): string => {
return isTestingLanguage() ? xTranslate(n === 1 ? str1 : strN) : agama.ngettext(str1, strN, n);
};
@@ -120,22 +118,22 @@ const n_ = (str1, strN, n) => {
* // here the string will be translated using the current locale
* return
Result: {_(result)}
;
*
- * @param {string} str the input string
- * @return {string} the input string
+ * @param str the input string
+ * @return the input string
*/
-const N_ = (str) => str;
+const N_ = (str: string): string => str;
/**
* Similar to the N_() function, but for the singular and plural form.
*
* @see {@link N_} for further information
- * @param {string} str1 the input string in the singular form
- * @param {string} strN the input string in the plural form
- * @param {number} n the actual number which decides whether to use the
+ * @param str1 the input string in the singular form
+ * @param strN the input string in the plural form
+ * @param n the actual number which decides whether to use the
* singular or plural form
- * @return {string} the original text, either "string1" or "stringN" depending
+ * @return the original text, either "string1" or "stringN" depending
* on the value "num"
*/
-const Nn_ = (str1, strN, n) => (n === 1 ? str1 : strN);
+const Nn_ = (str1: string, strN: string, n: number): string => (n === 1 ? str1 : strN);
export { _, n_, N_, Nn_ };
From 1e777ec8fe72c750a54f354f5bd9a44b58887c62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 26 Dec 2024 17:19:08 +0000
Subject: [PATCH 045/147] refactor(web): migrate src/agama to TypeScript
---
web/src/agama.js | 99 --------------------------------------------
web/src/agama.ts | 99 ++++++++++++++++++++++++++++++++++++++++++++
web/src/i18n.test.ts | 16 +++----
3 files changed, 108 insertions(+), 106 deletions(-)
delete mode 100644 web/src/agama.js
create mode 100644 web/src/agama.ts
diff --git a/web/src/agama.js b/web/src/agama.js
deleted file mode 100644
index e89e0ded3f..0000000000
--- a/web/src/agama.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) [2024] SUSE LLC
- *
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, contact SUSE LLC.
- *
- * To contact SUSE LLC about this file by physical or electronic mail, you may
- * find current contact information at www.suse.com.
- */
-
-/**
- * This module provides a global "agama" object which can be use from other
- * scripts like "po.js".
- */
-
-const agama = {
- // the current language
- language: "en",
-};
-
-// mapping with the current translations
-let translations = {};
-// function used for computing the plural form index
-let plural_fn;
-
-// set the current translations, called from po..js
-agama.locale = function locale(po) {
- if (po) {
- Object.assign(translations, po);
-
- const header = po[""];
- if (header) {
- if (header["plural-forms"]) plural_fn = header["plural-forms"];
- if (header.language) agama.language = header.language;
- }
- } else if (po === null) {
- translations = {};
- plural_fn = undefined;
- agama.language = "en";
- }
-};
-
-/**
- * get a translation for a singular text
- * @param {string} str input text
- * @return translated text or the original text if the translation is not found
- */
-agama.gettext = function gettext(str) {
- if (translations) {
- const translated = translations[str];
- if (translated?.[0]) return translated[0];
- }
-
- // fallback, return the original text
- return str;
-};
-
-/**
- * get a translation for a plural text
- * @param {string} str1 input singular text
- * @param {string} strN input plural text
- * @param {number} n the actual number which decides whether to use the
- * singular or plural form (of which plural form if there are several of them)
- * @return translated text or the original text if the translation is not found
- */
-agama.ngettext = function ngettext(str1, strN, n) {
- if (translations && plural_fn) {
- // plural form translations are indexed by the singular variant
- const translation = translations[str1];
-
- if (translation) {
- const plural_index = plural_fn(n);
-
- // the plural function either returns direct index (integer) in the plural
- // translations or a boolean indicating simple plural form which
- // needs to be converted to index 0 (singular) or 1 (plural)
- const index = plural_index === true ? 1 : plural_index || 0;
-
- if (translation[index]) return translation[index];
- }
- }
-
- // fallback, return the original text
- return n === 1 ? str1 : strN;
-};
-
-export default agama;
diff --git a/web/src/agama.ts b/web/src/agama.ts
new file mode 100644
index 0000000000..9ecc8a57cf
--- /dev/null
+++ b/web/src/agama.ts
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) [2024] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+/**
+ * This module provides a global "agama" object which can be use from other
+ * scripts like "po.js".
+ */
+
+// mapping with the current translations
+let translations = {};
+// function used for computing the plural form index
+let plural_fn: (n: number) => boolean;
+
+const agama = {
+ // the current language
+ language: "en",
+
+ // set the current translations, called from po..js
+ locale: (po) => {
+ if (po) {
+ Object.assign(translations, po);
+
+ const header = po[""];
+ if (header) {
+ if (header["plural-forms"]) plural_fn = header["plural-forms"];
+ if (header.language) agama.language = header.language;
+ }
+ } else if (po === null) {
+ translations = {};
+ plural_fn = undefined;
+ agama.language = "en";
+ }
+ },
+
+ /**
+ * Get a translation for a singular text
+ * @param str input text
+ * @return translated text or the original text if the translation is not found
+ */
+ gettext: (str: string): string => {
+ if (translations) {
+ const translated = translations[str];
+ if (translated?.[0]) return translated[0];
+ }
+
+ // fallback, return the original text
+ return str;
+ },
+
+ /**
+ * get a translation for a plural text
+ * @param str1 input singular text
+ * @param strN input plural text
+ * @param n the actual number which decides whether to use the
+ * singular or plural form (of which plural form if there are several of them)
+ * @return translated text or the original text if the translation is not found
+ */
+ ngettext: (str1: string, strN: string, n: number) => {
+ if (translations && plural_fn) {
+ // plural form translations are indexed by the singular variant
+ const translation = translations[str1];
+
+ if (translation) {
+ const plural_index = plural_fn(n);
+
+ // the plural function either returns direct index (integer) in the plural
+ // translations or a boolean indicating simple plural form which
+ // needs to be converted to index 0 (singular) or 1 (plural)
+ const index = plural_index === true ? 1 : plural_index || 0;
+
+ if (translation[index]) return translation[index];
+ }
+ }
+
+ // fallback, return the original text
+ return n === 1 ? str1 : strN;
+ },
+};
+
+export default agama;
diff --git a/web/src/i18n.test.ts b/web/src/i18n.test.ts
index 6c620a30b1..c3caef75e2 100644
--- a/web/src/i18n.test.ts
+++ b/web/src/i18n.test.ts
@@ -20,15 +20,17 @@
* find current contact information at www.suse.com.
*/
+/* eslint-disable agama-i18n/string-literals */
+
import { _, n_, N_, Nn_ } from "~/i18n";
import agama from "~/agama";
// mock the cockpit gettext functions
-jest.mock("~/agama");
-const gettextFn = jest.fn();
-agama.gettext.mockImplementation(gettextFn);
-const ngettextFn = jest.fn();
-agama.ngettext.mockImplementation(ngettextFn);
+jest.mock("~/agama", () => ({
+ ...jest.requireActual("~/agama"),
+ gettext: jest.fn(),
+ ngettext: jest.fn(),
+}));
// some testing texts
const text = "text to translate";
@@ -40,7 +42,7 @@ describe("i18n", () => {
it("calls the agama.gettext() implementation", () => {
_(text);
- expect(gettextFn).toHaveBeenCalledWith(text);
+ expect(agama.gettext).toHaveBeenCalledWith(text);
});
});
@@ -48,7 +50,7 @@ describe("i18n", () => {
it("calls the agama.ngettext() implementation", () => {
n_(singularText, pluralText, 1);
- expect(ngettextFn).toHaveBeenCalledWith(singularText, pluralText, 1);
+ expect(agama.ngettext).toHaveBeenCalledWith(singularText, pluralText, 1);
});
});
From fd989ffa23e50cdc00308e3722f7ede87f5df085 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 09:00:29 +0000
Subject: [PATCH 046/147] refactor(web): migrate src/utils to TypeScript
---
web/src/components/l10n/TimezoneSelection.tsx | 2 +-
web/src/{utils.test.js => utils.test.ts} | 25 +---
web/src/{utils.js => utils.ts} | 137 ++++++++----------
3 files changed, 62 insertions(+), 102 deletions(-)
rename web/src/{utils.test.js => utils.test.ts} (88%)
rename web/src/{utils.js => utils.ts} (75%)
diff --git a/web/src/components/l10n/TimezoneSelection.tsx b/web/src/components/l10n/TimezoneSelection.tsx
index 0ea385a2ba..614ba8dc5a 100644
--- a/web/src/components/l10n/TimezoneSelection.tsx
+++ b/web/src/components/l10n/TimezoneSelection.tsx
@@ -98,7 +98,7 @@ export default function TimezoneSelection() {
}
description={
- {timezoneTime(id, { date }) || ""}
+ {timezoneTime(id, date) || ""}
{details}
diff --git a/web/src/utils.test.js b/web/src/utils.test.ts
similarity index 88%
rename from web/src/utils.test.js
rename to web/src/utils.test.ts
index 80003d8356..99ced7f4e0 100644
--- a/web/src/utils.test.js
+++ b/web/src/utils.test.ts
@@ -28,7 +28,6 @@ import {
noop,
toValidationError,
localConnection,
- remoteConnection,
isObject,
slugify,
} from "./utils";
@@ -43,7 +42,7 @@ describe("noop", () => {
describe("partition", () => {
it("returns two groups of elements that do and do not satisfy provided filter", () => {
const numbers = [1, 2, 3, 4, 5, 6];
- const [odd, even] = partition(numbers, (number) => number % 2);
+ const [odd, even] = partition(numbers, (number) => number % 2 !== 0);
expect(odd).toEqual([1, 3, 5]);
expect(even).toEqual([2, 4, 6]);
@@ -126,26 +125,6 @@ describe("localConnection", () => {
});
});
-describe("remoteConnection", () => {
- describe("when the page URL is " + localURL, () => {
- it("returns true", () => {
- expect(remoteConnection(localURL)).toEqual(false);
- });
- });
-
- describe("when the page URL is " + localURL2, () => {
- it("returns true", () => {
- expect(remoteConnection(localURL2)).toEqual(false);
- });
- });
-
- describe("when the page URL is " + remoteURL, () => {
- it("returns false", () => {
- expect(remoteConnection(remoteURL)).toEqual(true);
- });
- });
-});
-
describe("isObject", () => {
it("returns true when called with an object", () => {
expect(isObject({ dummy: "object" })).toBe(true);
@@ -156,7 +135,7 @@ describe("isObject", () => {
});
it("returns false when called with undefined", () => {
- expect(isObject()).toBe(false);
+ expect(isObject(undefined)).toBe(false);
});
it("returns false when called with a string", () => {
diff --git a/web/src/utils.js b/web/src/utils.ts
similarity index 75%
rename from web/src/utils.js
rename to web/src/utils.ts
index 50feed2b79..457ef5b99a 100644
--- a/web/src/utils.js
+++ b/web/src/utils.ts
@@ -28,8 +28,8 @@ import { useEffect, useRef, useCallback, useState } from "react";
*
* Borrowed from https://dev.to/alesm0101/how-to-check-if-a-value-is-an-object-in-javascript-3pin
*
- * @param {any} value - the value to be checked
- * @return {boolean} true when given value is an object; false otherwise
+ * @param value - the value to be checked
+ * @return true when given value is an object; false otherwise
*/
const isObject = (value) =>
typeof value === "object" &&
@@ -43,18 +43,18 @@ const isObject = (value) =>
/**
* Whether given object is empty or not
*
- * @param {object} value - the value to be checked
- * @return {boolean} true when given value is an empty object; false otherwise
+ * @param value - the value to be checked
+ * @return true when given value is an empty object; false otherwise
*/
-const isObjectEmpty = (value) => {
+const isObjectEmpty = (value: object) => {
return Object.keys(value).length === 0;
};
/**
* Whether given value is empty or not
*
- * @param {object} value - the value to be checked
- * @return {boolean} false if value is a function, a not empty object, or a not
+ * @param value - the value to be checked
+ * @return false if value is a function, a not empty object, or a not
* empty string; true otherwise
*/
const isEmpty = (value) => {
@@ -84,12 +84,12 @@ const isEmpty = (value) => {
/**
* Returns an empty function useful to be used as a default callback.
*
- * @return {function} empty function
+ * @return empty function
*/
const noop = () => undefined;
/**
- * @return {function} identity function
+ * @return identity function
*/
const identity = (i) => i;
@@ -97,11 +97,14 @@ const identity = (i) => i;
* Returns a new array with a given collection split into two groups, the first holding elements
* satisfying the filter and the second with those which do not.
*
- * @param {Array} collection - the collection to be filtered
- * @param {function} filter - the function to be used as filter
- * @return {Array[]} a pair of arrays, [passing, failing]
+ * @param collection - the collection to be filtered
+ * @param filter - the function to be used as filter
+ * @return a pair of arrays, [passing, failing]
*/
-const partition = (collection, filter) => {
+const partition = (
+ collection: Array,
+ filter: (element: T) => boolean,
+): [Array, Array] => {
const pass = [];
const fail = [];
@@ -114,23 +117,17 @@ const partition = (collection, filter) => {
/**
* Generates a new array without null and undefined values.
- *
- * @param {Array} collection
- * @returns {Array}
*/
-function compact(collection) {
+const compact = (collection: Array) => {
return collection.filter((e) => e !== null && e !== undefined);
-}
+};
/**
* Generates a new array without duplicates.
- *
- * @param {Array} collection
- * @returns {Array}
*/
-function uniq(collection) {
+const uniq = (collection: Array) => {
return [...new Set(collection)];
-}
+};
/**
* Simple utility function to help building className conditionally
@@ -141,12 +138,12 @@ function uniq(collection) {
*
* @todo Use https://github.com/JedWatson/classnames instead?
*
- * @param {...*} classes - CSS classes to join
- * @returns {String} - CSS classes joined together after ignoring falsy values
+ * @param classes - CSS classes to join
+ * @returns CSS classes joined together after ignoring falsy values
*/
-function classNames(...classes) {
+const classNames = (...classes) => {
return classes.filter((item) => !!item).join(" ");
-}
+};
/**
* Convert any string into a slug
@@ -157,10 +154,10 @@ function classNames(...classes) {
* slugify("Agama! / Network 1");
* // returns "agama-network-1"
*
- * @param {string} input - the string to slugify
- * @returns {string} - the slug
+ * @param input - the string to slugify
+ * @returns the slug
*/
-function slugify(input) {
+const slugify = (input: string) => {
if (!input) return "";
return (
@@ -177,26 +174,24 @@ function slugify(input) {
// replace multiple spaces or hyphens with a single hyphen
.replace(/[\s-]+/g, "-")
);
-}
+};
-/**
- * @typedef {Object} cancellableWrapper
- * @property {Promise} promise - Cancellable promise
- * @property {function} cancel - Function for canceling the promise
- */
+type CancellableWrapper = {
+ /** Cancellable promise */
+ promise: Promise;
+ /** Function for cancelling the promise */
+ cancel: Function;
+};
/**
* Creates a wrapper object with a cancellable promise and a function for canceling the promise
*
* @see useCancellablePromise
- *
- * @param {Promise} promise
- * @returns {cancellableWrapper}
*/
-function makeCancellable(promise) {
+const makeCancellable = (promise: Promise): CancellableWrapper => {
let isCanceled = false;
- const cancellablePromise = new Promise((resolve, reject) => {
+ const cancellablePromise: Promise = new Promise((resolve, reject) => {
promise
.then((value) => !isCanceled && resolve(value))
.catch((error) => !isCanceled && reject(error));
@@ -208,7 +203,7 @@ function makeCancellable(promise) {
isCanceled = true;
},
};
-}
+};
/**
* Allows using promises in a safer way.
@@ -217,7 +212,7 @@ function makeCancellable(promise) {
* a promise (e.g., setting the component state once a D-Bus call is answered). Note that nothing
* guarantees that a React component is still mounted when a promise is resolved.
*
- * @see {@link https://overreacted.io/a-complete-guide-to-useeffect/#speaking-of-race-conditions|Race conditions}
+ * @see {@link https://overreacted.io/a-complete-guide-to-useeffect/#speaking-of-race-conditions|Race conditions}
*
* The hook provides a function for making promises cancellable. All cancellable promises are
* automatically canceled once the component is unmounted. Note that the promises are not really
@@ -238,8 +233,8 @@ function makeCancellable(promise) {
* cancellablePromise(promise).then(setState);
* }, [setState, cancellablePromise]);
*/
-function useCancellablePromise() {
- const promises = useRef();
+const useCancellablePromise = () => {
+ const promises = useRef>>();
useEffect(() => {
promises.current = [];
@@ -251,22 +246,22 @@ function useCancellablePromise() {
}, []);
const cancellablePromise = useCallback((promise) => {
- const cancellableWrapper = makeCancellable(promise);
+ const cancellableWrapper: CancellableWrapper = makeCancellable(promise);
promises.current.push(cancellableWrapper);
return cancellableWrapper.promise;
}, []);
return { cancellablePromise };
-}
+};
/** Hook for using local storage
*
* @see {@link https://www.robinwieruch.de/react-uselocalstorage-hook/}
*
- * @param {String} storageKey
- * @param {*} fallbackState
+ * @param storageKey
+ * @param fallbackState
*/
-const useLocalStorage = (storageKey, fallbackState) => {
+const useLocalStorage = (storageKey: string, fallbackState) => {
const [value, setValue] = useState(JSON.parse(localStorage.getItem(storageKey)) ?? fallbackState);
useEffect(() => {
@@ -281,9 +276,8 @@ const useLocalStorage = (storageKey, fallbackState) => {
*
* Source {@link https://designtechworld.medium.com/create-a-custom-debounce-hook-in-react-114f3f245260}
*
- * @param {Function} callback - Function to be called after some delay.
- * @param {number} delay - Delay in milliseconds.
- * @returns {Function}
+ * @param callback - Function to be called after some delay.
+ * @param delay - Delay in milliseconds.
*
* @example
*
@@ -291,7 +285,7 @@ const useLocalStorage = (storageKey, fallbackState) => {
* log("test ", 1) // The message will be logged after at least 1 second.
* log("test ", 2) // Subsequent calls cancels pending calls.
*/
-const useDebounce = (callback, delay) => {
+const useDebounce = (callback: Function, delay: number) => {
const timeoutRef = useRef(null);
useEffect(() => {
@@ -317,10 +311,9 @@ const useDebounce = (callback, delay) => {
};
/**
- * @param {string}
- * @returns {number}
+ * Convert given string to a hexadecimal number
*/
-const hex = (value) => {
+const hex = (value: string) => {
const sanitizedValue = value.replaceAll(".", "");
return parseInt(sanitizedValue, 16);
};
@@ -357,14 +350,14 @@ const locationReload = () => {
* - https://github.com/jsdom/jsdom/blob/master/Changelog.md#2100
* - https://github.com/jsdom/jsdom/issues/3492
*
- * @param {string} query
+ * @param query
*/
-const setLocationSearch = (query) => {
+const setLocationSearch = (query: string) => {
window.location.search = query;
};
/**
- * Is the Agama server running locally?
+ * WetherAgama server is running locally or not.
*
* This function should be used only in special cases, the Agama behavior should
* be the same regardless of the user connection.
@@ -373,9 +366,9 @@ const setLocationSearch = (query) => {
* environment variable to `1`. This can be useful for debugging or for
* development.
*
- * @returns {boolean} `true` if the connection is local, `false` otherwise
+ * @returns `true` if the connection is local, `false` otherwise
*/
-const localConnection = (location = window.location) => {
+const localConnection = (location: Location | URL = window.location) => {
// forced local behavior
if (process.env.LOCAL_CONNECTION === "1") return true;
@@ -385,26 +378,15 @@ const localConnection = (location = window.location) => {
return hostname === "localhost" || hostname.startsWith("127.");
};
-/**
- * Is the Agama server running remotely?
- *
- * @see localConnection
- *
- * @returns {boolean} `true` if the connection is remote, `false` otherwise
- */
-const remoteConnection = (...args) => !localConnection(...args);
-
/**
* Time for the given timezone.
*
- * @param {string} timezone - E.g., "Atlantic/Canary".
- * @param {object} [options]
- * @param {Date} options.date - Date to take the time from.
+ * @param timezone - E.g., "Atlantic/Canary".
+ * @param date - Date to take the time from.
*
- * @returns {string|undefined} - Time in 24 hours format (e.g., "23:56"). Undefined for an unknown
- * timezone.
+ * @returns Time in 24 hours format (e.g., "23:56"). Undefined for an unknown timezone.
*/
-const timezoneTime = (timezone, { date = new Date() }) => {
+const timezoneTime = (timezone: string, date: Date = new Date()): string | undefined => {
try {
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
@@ -438,7 +420,6 @@ export {
locationReload,
setLocationSearch,
localConnection,
- remoteConnection,
slugify,
timezoneTime,
};
From 5c38fc895696c00b898c459682784bdf6ff75269 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 11:28:37 +0000
Subject: [PATCH 047/147] refactor(web): migrate test-utils to TypeScript
---
.../{test-utils.test.js => test-utils.test.tsx} | 5 -----
web/src/{test-utils.js => test-utils.tsx} | 14 +++++++-------
2 files changed, 7 insertions(+), 12 deletions(-)
rename web/src/{test-utils.test.js => test-utils.test.tsx} (89%)
rename web/src/{test-utils.js => test-utils.tsx} (92%)
diff --git a/web/src/test-utils.test.js b/web/src/test-utils.test.tsx
similarity index 89%
rename from web/src/test-utils.test.js
rename to web/src/test-utils.test.tsx
index 5c89a5181d..f8baaa092e 100644
--- a/web/src/test-utils.test.js
+++ b/web/src/test-utils.test.tsx
@@ -40,11 +40,6 @@ describe("resetLocalStorage", () => {
expect(window.localStorage.setItem).not.toHaveBeenCalled();
});
- it("does not set an initial state if given value is not an object", () => {
- resetLocalStorage(["wrong", "initial state"]);
- expect(window.localStorage.setItem).not.toHaveBeenCalled();
- });
-
it("sets an initial state if given value is an object", () => {
resetLocalStorage({
storage: "something",
diff --git a/web/src/test-utils.js b/web/src/test-utils.tsx
similarity index 92%
rename from web/src/test-utils.js
rename to web/src/test-utils.tsx
index 7580c0be32..01a69857d3 100644
--- a/web/src/test-utils.js
+++ b/web/src/test-utils.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022-2023] SUSE LLC
+ * Copyright (c) [2022-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -112,7 +112,7 @@ const Providers = ({ children, withL10n }) => {
*
* @see #plainRender for rendering without installer providers
*/
-const installerRender = (ui, options = {}) => {
+const installerRender = (ui: React.ReactNode, options: { withL10n?: boolean } = {}) => {
const queryClient = new QueryClient({});
const Wrapper = ({ children }) => (
@@ -159,11 +159,11 @@ const plainRender = (ui, options = {}) => {
* It can be useful to mock functions that might receive a callback that you can
* execute on-demand during the test.
*
- * @return {[() => () => void, Array<(any) => void>]} a tuple with the mocked function and the list of callbacks.
+ * @return a tuple with the mocked function and the list of callbacks.
*/
-const createCallbackMock = () => {
+const createCallbackMock = (): [(callback: Function) => () => void, Array<(arg0: any) => void>] => {
const callbacks = [];
- const on = (callback) => {
+ const on = (callback: Function) => {
callbacks.push(callback);
return () => {
const position = callbacks.indexOf(callback);
@@ -176,10 +176,10 @@ const createCallbackMock = () => {
/**
* Helper for clearing window.localStorage and setting an initial state if needed.
*
- * @param {Object.} [initialState] - a collection of keys/values as
+ * @param [initialState] - a collection of keys/values as
* expected by {@link https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem Web Storage API setItem method}
*/
-const resetLocalStorage = (initialState) => {
+const resetLocalStorage = (initialState?: { [key: string]: string }) => {
window.localStorage.clear();
if (!isObject(initialState)) return;
From 7ecdd9ec96af70aa29009bac4aed16b6aa5f8cea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 12:23:04 +0000
Subject: [PATCH 048/147] refactor utils
---
web/src/components/core/Popup.tsx | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/web/src/components/core/Popup.tsx b/web/src/components/core/Popup.tsx
index 9135bfcb6c..ec2ba7f6a8 100644
--- a/web/src/components/core/Popup.tsx
+++ b/web/src/components/core/Popup.tsx
@@ -20,7 +20,7 @@
* find current contact information at www.suse.com.
*/
-import React from "react";
+import React, { isValidElement } from "react";
import { Button, ButtonProps, Modal, ModalProps } from "@patternfly/react-core";
import { Loading } from "~/components/layout";
import { _ } from "~/i18n";
@@ -202,9 +202,8 @@ const Popup = ({
children,
...props
}: PopupProps) => {
- const [actions, content] = partition(
- React.Children.toArray(children),
- (child) => child.type === Actions,
+ const [actions, content] = partition(React.Children.toArray(children), (child) =>
+ isValidElement(child) ? child.type === Actions : false,
);
return (
From 4f8dcdb4ad5f49cf822c56ebdcac678d8580d82a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 14:16:17 +0000
Subject: [PATCH 049/147] fix(web): adjust some types in ZFCP components
---
web/src/components/storage/zfcp/ZFCPDiskActivationPage.tsx | 4 ++--
web/src/components/storage/zfcp/ZFCPDiskForm.tsx | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/web/src/components/storage/zfcp/ZFCPDiskActivationPage.tsx b/web/src/components/storage/zfcp/ZFCPDiskActivationPage.tsx
index 4026d5aeea..7ffa368ffb 100644
--- a/web/src/components/storage/zfcp/ZFCPDiskActivationPage.tsx
+++ b/web/src/components/storage/zfcp/ZFCPDiskActivationPage.tsx
@@ -41,9 +41,9 @@ export default function ZFCPDiskActivationPage() {
const onSubmit = async (formData: LUNInfo & { id: string }) => {
setIsAcceptDisabled(true);
- const result = await cancellablePromise(
+ const result = (await cancellablePromise(
activateZFCPDisk(formData.id, formData.wwpn, formData.lun),
- );
+ )) as Awaited>;
if (result.status === 200) navigate(PATHS.zfcp.root);
setIsAcceptDisabled(false);
diff --git a/web/src/components/storage/zfcp/ZFCPDiskForm.tsx b/web/src/components/storage/zfcp/ZFCPDiskForm.tsx
index 2ac341996e..19f5ab3c4f 100644
--- a/web/src/components/storage/zfcp/ZFCPDiskForm.tsx
+++ b/web/src/components/storage/zfcp/ZFCPDiskForm.tsx
@@ -24,7 +24,7 @@
import React, { FormEvent, useEffect, useState } from "react";
import { Alert, Form, FormGroup, FormSelect, FormSelectOption } from "@patternfly/react-core";
-import { AxiosResponseHeaders } from "axios";
+import { AxiosResponse } from "axios";
import { Page } from "~/components/core";
import { useZFCPControllers, useZFCPDisks } from "~/queries/storage/zfcp";
import { inactiveLuns } from "~/utils/zfcp";
@@ -46,7 +46,7 @@ export default function ZFCPDiskForm({
onLoading,
}: {
id: string;
- onSubmit: (formData: FormData) => Promise;
+ onSubmit: (formData: FormData) => Promise;
onLoading: (isLoading: boolean) => void;
}) {
const controllers = useZFCPControllers();
From 8c63e13c7fb4cc7431a549d0ecbecbb000e7aa53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 14:22:33 +0000
Subject: [PATCH 050/147] fix(web): type adjustments
---
web/src/components/network/NetworkPage.tsx | 2 +-
web/src/utils.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/web/src/components/network/NetworkPage.tsx b/web/src/components/network/NetworkPage.tsx
index f28015222a..83c3a16166 100644
--- a/web/src/components/network/NetworkPage.tsx
+++ b/web/src/components/network/NetworkPage.tsx
@@ -98,7 +98,7 @@ const NoWifiAvailable = () => (
export default function NetworkPage() {
useNetworkConfigChanges();
const { connections, devices, settings } = useNetwork();
- const [wifiConnections, wiredConnections] = partition(connections, (c) => c.wireless);
+ const [wifiConnections, wiredConnections] = partition(connections, (c) => !!c.wireless);
return (
diff --git a/web/src/utils.ts b/web/src/utils.ts
index 457ef5b99a..3290d65117 100644
--- a/web/src/utils.ts
+++ b/web/src/utils.ts
@@ -245,7 +245,7 @@ const useCancellablePromise = () => {
};
}, []);
- const cancellablePromise = useCallback((promise) => {
+ const cancellablePromise = useCallback((promise: Promise): Promise => {
const cancellableWrapper: CancellableWrapper = makeCancellable(promise);
promises.current.push(cancellableWrapper);
return cancellableWrapper.promise;
From b6e89843be13dc455ac2d5ea4cd38119685f6cd4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 14:42:56 +0000
Subject: [PATCH 051/147] fix(web): please ESLint
---
web/src/test-utils.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/web/src/test-utils.tsx b/web/src/test-utils.tsx
index 01a69857d3..39493e288e 100644
--- a/web/src/test-utils.tsx
+++ b/web/src/test-utils.tsx
@@ -20,6 +20,8 @@
* find current contact information at www.suse.com.
*/
+/* eslint-disable i18next/no-literal-string */
+
/**
* A module for providing utility functions for testing
*
@@ -161,7 +163,7 @@ const plainRender = (ui, options = {}) => {
*
* @return a tuple with the mocked function and the list of callbacks.
*/
-const createCallbackMock = (): [(callback: Function) => () => void, Array<(arg0: any) => void>] => {
+const createCallbackMock = (): [(callback: Function) => () => void, Array<(arg0) => void>] => {
const callbacks = [];
const on = (callback: Function) => {
callbacks.push(callback);
From 02b4a4bbb8b33e7ec1a8e525d3513e72ae56a428 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 14:55:50 +0000
Subject: [PATCH 052/147] refactor(web): migrate router to TypeScript
---
web/src/{router.js => router.tsx} | 1 -
1 file changed, 1 deletion(-)
rename web/src/{router.js => router.tsx} (99%)
diff --git a/web/src/router.js b/web/src/router.tsx
similarity index 99%
rename from web/src/router.js
rename to web/src/router.tsx
index f949a76652..8a94477885 100644
--- a/web/src/router.js
+++ b/web/src/router.tsx
@@ -99,7 +99,6 @@ const protectedRoutes = () => [
const router = () =>
createHashRouter([
{
- exact: true,
path: PATHS.login,
element: (
From 96f3db659f50bd366d7ef4be3325606314d48ca9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 15:03:49 +0000
Subject: [PATCH 053/147] refactor(web): migrate src/index to TypeScript
---
web/src/{index.js => index.tsx} | 0
web/webpack.config.js | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename web/src/{index.js => index.tsx} (100%)
diff --git a/web/src/index.js b/web/src/index.tsx
similarity index 100%
rename from web/src/index.js
rename to web/src/index.tsx
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 1f7db8f1a6..d92d5291f9 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -101,7 +101,7 @@ module.exports = {
ignored: /node_modules/,
},
entry: {
- index: ["./src/index.js"],
+ index: ["./src/index.tsx"],
},
devServer: {
hot: true,
From 6ccb663c2b2a1ee7350e38a23b8270be6bb83f61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 27 Dec 2024 15:13:41 +0000
Subject: [PATCH 054/147] refactor(web): migrate setupTests to TypeScript
---
web/jest.config.js | 2 +-
web/src/{setupTests.js => setupTests.ts} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename web/src/{setupTests.js => setupTests.ts} (100%)
diff --git a/web/jest.config.js b/web/jest.config.js
index 670ff97b57..728e681612 100644
--- a/web/jest.config.js
+++ b/web/jest.config.js
@@ -135,7 +135,7 @@ module.exports = {
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
- setupFilesAfterEnv: ["/src/setupTests.js"],
+ setupFilesAfterEnv: ["/src/setupTests.ts"],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
diff --git a/web/src/setupTests.js b/web/src/setupTests.ts
similarity index 100%
rename from web/src/setupTests.js
rename to web/src/setupTests.ts
From 21d52475d62efc47379354da5a3a9b72ab534623 Mon Sep 17 00:00:00 2001
From: YaST Bot
Date: Sun, 29 Dec 2024 03:15:10 +0000
Subject: [PATCH 055/147] Update service PO files
Agama-weblate commit: fccd059f5a372d063f8fd431fb98b8edc58db734
---
service/po/id.po | 67 +++++++++++++++++++++++++++---------------------
1 file changed, 38 insertions(+), 29 deletions(-)
diff --git a/service/po/id.po b/service/po/id.po
index 89f49fdbd9..78470592e0 100644
--- a/service/po/id.po
+++ b/service/po/id.po
@@ -8,50 +8,48 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-12-22 02:45+0000\n"
-"PO-Revision-Date: 2023-12-28 21:02+0000\n"
+"PO-Revision-Date: 2024-12-25 18:50+0000\n"
"Last-Translator: Arif Budiman \n"
-"Language-Team: Indonesian \n"
+"Language-Team: Indonesian \n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.9.1\n"
+"X-Generator: Weblate 5.9.2\n"
#. Runs the config phase
#: service/lib/agama/manager.rb:93
msgid "Analyze disks"
-msgstr ""
+msgstr "Menganalisis disk"
#: service/lib/agama/manager.rb:93
-#, fuzzy
msgid "Configure software"
-msgstr "Memeriksa Perangkat Lunak"
+msgstr "Mengkonfigurasi perangkat lunak"
#. Runs the install phase
#. rubocop:disable Metrics/AbcSize
#: service/lib/agama/manager.rb:116
msgid "Prepare disks"
-msgstr ""
+msgstr "Siapkan disk"
#: service/lib/agama/manager.rb:117
-#, fuzzy
msgid "Install software"
-msgstr "Menginstal Perangkat Lunak"
+msgstr "Menginstal perangkat lunak"
#: service/lib/agama/manager.rb:118
msgid "Configure the system"
-msgstr ""
+msgstr "Mengkonfigurasi sistem"
#. rubocop:enable Metrics/AbcSize
#: service/lib/agama/manager.rb:156
msgid "Load software translations"
-msgstr ""
+msgstr "Memuat terjemahan perangkat lunak"
#: service/lib/agama/manager.rb:157
msgid "Load storage translations"
-msgstr ""
+msgstr "Memuat terjemahan penyimpanan"
#. Callback to handle unsigned files
#.
@@ -60,12 +58,12 @@ msgstr ""
#: service/lib/agama/software/callbacks/signature.rb:63
#, perl-brace-format
msgid "The file %{filename} from repository %{repo_name} (%{repo_url})"
-msgstr "File %{filename} dari repositori %{repo_name} (%{repo_url})"
+msgstr "Berkas %{filename} dari repositori %{repo_name} (%{repo_url})"
#: service/lib/agama/software/callbacks/signature.rb:67
#, perl-brace-format
msgid "The file %{filename}"
-msgstr "File %{filename}"
+msgstr "Berkas %{filename}"
#: service/lib/agama/software/callbacks/signature.rb:71
#, perl-brace-format
@@ -73,7 +71,7 @@ msgid ""
"%{source} is not digitally signed. The origin and integrity of the file "
"cannot be verified. Use it anyway?"
msgstr ""
-"%{source} tidak ditandatangani secara digital. Asal dan integritas file "
+"%{source} tidak ditandatangani secara digital. Asal dan integritas berkas "
"tidak dapat diverifikasi. Tetap menggunakannya?"
#. Callback to handle signature verification failures
@@ -140,24 +138,24 @@ msgstr "Ditemukan %s masalah ketergantungan."
#. @return [Agama::Issue]
#: service/lib/agama/storage/config_checker.rb:87
msgid "No device found for a mandatory drive"
-msgstr ""
+msgstr "Perangkat tidak ditemukan untuk drive wajib"
#: service/lib/agama/storage/config_checker.rb:89
msgid "No device found for a mandatory partition"
-msgstr ""
+msgstr "Perangkat tidak ditemukan untuk partisi wajib"
#. TRANSLATORS: %s is the replaced by a mount path (e.g., "/home").
#: service/lib/agama/storage/config_checker.rb:118
#, c-format
msgid "Missing file system type for '%s'"
-msgstr ""
+msgstr "Tidak ada tipe sistem berkas untuk '%s'"
#. TRANSLATORS: %{filesystem} is replaced by a file system type (e.g., "Btrfs") and
#. %{path} is replaced by a mount path (e.g., "/home").
#: service/lib/agama/storage/config_checker.rb:145
#, perl-brace-format
msgid "The file system type '%{filesystem}' is not suitable for '%{path}'"
-msgstr ""
+msgstr "Tipe sistem berkas '%{filesystem}' tidak cocok untuk '%{path}'"
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
@@ -166,20 +164,22 @@ msgstr ""
msgid ""
"No passphrase provided (required for using the method '%{crypt_method}')."
msgstr ""
+"Kata sandi tidak disediakan (diperlukan untuk menggunakan metode "
+"'%{crypt_method}')."
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
#: service/lib/agama/storage/config_checker.rb:196
#, perl-brace-format
msgid "Encryption method '%{crypt_method}' is not available in this system."
-msgstr ""
+msgstr "Metode enkripsi '%{crypt_method}' tidak tersedia di sistem ini."
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1', 'random_swap').
#: service/lib/agama/storage/config_checker.rb:226
#, perl-brace-format
msgid "'%{crypt_method}' is not a suitable method to encrypt the device."
-msgstr ""
+msgstr "'%{crypt_method}' bukan metode yang cocok untuk mengenkripsi perangkat."
#. TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
#: service/lib/agama/storage/config_checker.rb:276
@@ -187,24 +187,26 @@ msgstr ""
msgid ""
"The device '%s' is used several times as target device for physical volumes"
msgstr ""
+"Perangkat '%s' digunakan beberapa kali sebagai perangkat target untuk volume "
+"fisik"
#. TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
#: service/lib/agama/storage/config_checker.rb:350
#, c-format
msgid "There is no LVM thin pool volume with alias '%s'"
-msgstr ""
+msgstr "Tidak ada volume pool tipis LVM dengan alias '%s'"
#. TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
#: service/lib/agama/storage/config_checker.rb:375
#, c-format
msgid "There is no LVM physical volume with alias '%s'"
-msgstr ""
+msgstr "Tidak ada volume fisik LVM dengan alias '%s'"
#. TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
#: service/lib/agama/storage/config_checker.rb:401
#, c-format
msgid "There is no target device for LVM physical volumes with alias '%s'"
-msgstr ""
+msgstr "Tidak ada perangkat target untuk volume fisik LVM dengan alias '%s'"
#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
#. (e.g., 'luks1').
@@ -213,6 +215,7 @@ msgstr ""
msgid ""
"'%{crypt_method}' is not a suitable method to encrypt the physical volumes."
msgstr ""
+"'%{crypt_method}' bukan metode yang cocok untuk mengenkripsi volume fisik."
#. Text of the reason preventing to shrink because there is no content.
#.
@@ -223,13 +226,17 @@ msgid ""
"case the device does contain a file system or a storage system that is not "
"supported, resizing will most likely cause data loss."
msgstr ""
+"Baik sistem berkas maupun sistem penyimpanan tidak terdeteksi pada "
+"perangkat. Jika perangkat memiliki sistem berkas atau sistem penyimpanan "
+"yang tidak didukung, mengubah ukuran kemungkinan besar akan menyebabkan "
+"hilangnya data."
#. Text of the reason preventing to shrink because there is no valid minimum size.
#.
#. @return [String, nil] nil if there is a minimum size or there is any other reasons.
#: service/lib/agama/storage/device_shrinking.rb:162
msgid "Shrinking is not supported by this device"
-msgstr ""
+msgstr "Penyusutan tidak didukung oleh perangkat ini"
#. Probes storage devices and performs an initial proposal
#: service/lib/agama/storage/manager.rb:120
@@ -271,21 +278,21 @@ msgstr "Menulis sysconfig bootloader"
#. @return [Issue]
#: service/lib/agama/storage/proposal.rb:287
msgid "Cannot accommodate the required file systems for installation"
-msgstr ""
+msgstr "Tidak dapat mengakomodasi sistem berkas yang diperlukan untuk instalasi"
#. Issue to communicate a generic Y2Storage error.
#.
#. @return [Issue]
#: service/lib/agama/storage/proposal.rb:298
msgid "A problem ocurred while calculating the storage setup"
-msgstr ""
+msgstr "Terjadi masalah saat menghitung pengaturan penyimpanan"
#. Returns an issue if there is no target device.
#.
#. @return [Issue, nil]
#: service/lib/agama/storage/proposal_strategies/guided.rb:127
msgid "No device selected for installation"
-msgstr ""
+msgstr "Tidak ada perangkat yang dipilih untuk pemasangan"
#. Returns an issue if any of the devices required for the proposal is not found
#.
@@ -296,12 +303,14 @@ msgid "The following selected device is not found in the system: %{devices}"
msgid_plural ""
"The following selected devices are not found in the system: %{devices}"
msgstr[0] ""
+"Perangkat yang dipilih berikut ini tidak ditemukan dalam sistem: %{devices}"
#. Recalculates the list of issues
#: service/lib/agama/users.rb:154
msgid ""
"Defining a user, setting the root password or a SSH public key is required"
msgstr ""
+"Wajib menentukan pengguna, mengatur kata sandi root atau kunci publik SSH"
#~ msgid "Probing Storage"
#~ msgstr "Memeriksa Penyimpanan"
From 815415dab66734f542fddf8218cf773e4bb9e16f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Mon, 30 Dec 2024 14:15:22 +0000
Subject: [PATCH 056/147] refactor(web): migrate hooks to TypeScript
---
web/src/hooks/useNodeSiblings.test.js | 63 ----------------
web/src/hooks/useNodeSiblings.test.tsx | 73 +++++++++++++++++++
...{useNodeSiblings.js => useNodeSiblings.ts} | 26 ++-----
3 files changed, 78 insertions(+), 84 deletions(-)
delete mode 100644 web/src/hooks/useNodeSiblings.test.js
create mode 100644 web/src/hooks/useNodeSiblings.test.tsx
rename web/src/hooks/{useNodeSiblings.js => useNodeSiblings.ts} (54%)
diff --git a/web/src/hooks/useNodeSiblings.test.js b/web/src/hooks/useNodeSiblings.test.js
deleted file mode 100644
index b2a569f845..0000000000
--- a/web/src/hooks/useNodeSiblings.test.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { renderHook } from "@testing-library/react";
-import useNodeSiblings from "./useNodeSiblings";
-
-// Mocked HTMLElement for testing
-const mockNode = {
- parentNode: {
- children: [
- { setAttribute: jest.fn(), removeAttribute: jest.fn() }, // sibling 1
- { setAttribute: jest.fn(), removeAttribute: jest.fn() }, // sibling 2
- { setAttribute: jest.fn(), removeAttribute: jest.fn() }, // sibling 3
- ],
- },
-};
-
-describe("useNodeSiblings", () => {
- it("should return noop functions when node is not provided", () => {
- const { result } = renderHook(() => useNodeSiblings(null));
- const [addAttribute, removeAttribute] = result.current;
-
- expect(addAttribute).toBeInstanceOf(Function);
- expect(removeAttribute).toBeInstanceOf(Function);
- expect(addAttribute).toEqual(expect.any(Function));
- expect(removeAttribute).toEqual(expect.any(Function));
-
- // Call the noop functions to ensure they don't throw any errors
- expect(() => addAttribute("attribute", "value")).not.toThrow();
- expect(() => removeAttribute("attribute")).not.toThrow();
- });
-
- it("should add attribute to all siblings when addAttribute is called", () => {
- const { result } = renderHook(() => useNodeSiblings(mockNode));
- const [addAttribute] = result.current;
- const attributeName = "attribute";
- const attributeValue = "value";
-
- addAttribute(attributeName, attributeValue);
-
- expect(mockNode.parentNode.children[0].setAttribute).toHaveBeenCalledWith(
- attributeName,
- attributeValue,
- );
- expect(mockNode.parentNode.children[1].setAttribute).toHaveBeenCalledWith(
- attributeName,
- attributeValue,
- );
- expect(mockNode.parentNode.children[2].setAttribute).toHaveBeenCalledWith(
- attributeName,
- attributeValue,
- );
- });
-
- it("should remove attribute from all siblings when removeAttribute is called", () => {
- const { result } = renderHook(() => useNodeSiblings(mockNode));
- const [, removeAttribute] = result.current;
- const attributeName = "attribute";
-
- removeAttribute(attributeName);
-
- expect(mockNode.parentNode.children[0].removeAttribute).toHaveBeenCalledWith(attributeName);
- expect(mockNode.parentNode.children[1].removeAttribute).toHaveBeenCalledWith(attributeName);
- expect(mockNode.parentNode.children[2].removeAttribute).toHaveBeenCalledWith(attributeName);
- });
-});
diff --git a/web/src/hooks/useNodeSiblings.test.tsx b/web/src/hooks/useNodeSiblings.test.tsx
new file mode 100644
index 0000000000..5052fd651b
--- /dev/null
+++ b/web/src/hooks/useNodeSiblings.test.tsx
@@ -0,0 +1,73 @@
+import React from "react";
+import { screen, renderHook } from "@testing-library/react";
+import useNodeSiblings from "./useNodeSiblings";
+import { plainRender } from "~/test-utils";
+
+const TestingComponent = () => (
+
+
+
+
+
+
+
+
+);
+
+describe("useNodeSiblings", () => {
+ it("should return noop functions when node is not provided", () => {
+ const { result } = renderHook(() => useNodeSiblings(null));
+ const [addAttribute, removeAttribute] = result.current;
+
+ expect(addAttribute).toBeInstanceOf(Function);
+ expect(removeAttribute).toBeInstanceOf(Function);
+ expect(addAttribute).toEqual(expect.any(Function));
+ expect(removeAttribute).toEqual(expect.any(Function));
+
+ // Call the noop functions to ensure they don't throw any errors
+ expect(() => addAttribute("attribute", "value")).not.toThrow();
+ expect(() => removeAttribute("attribute")).not.toThrow();
+ });
+
+ it("should add attribute to all siblings when addAttribute is called", () => {
+ plainRender();
+ const targetNode = screen.getByRole("region", { name: "Second sibling" });
+ const firstSibling = screen.getByRole("region", { name: "First sibling" });
+ const thirdSibling = screen.getByRole("region", { name: "Third sibling" });
+ const noSibling = screen.getByRole("region", { name: "Not a sibling" });
+ const { result } = renderHook(() => useNodeSiblings(targetNode));
+ const [addAttribute] = result.current;
+ const attributeName = "attribute";
+ const attributeValue = "value";
+
+ expect(firstSibling).not.toHaveAttribute(attributeName, attributeValue);
+ expect(thirdSibling).not.toHaveAttribute(attributeName, attributeValue);
+ expect(noSibling).not.toHaveAttribute(attributeName, attributeValue);
+
+ addAttribute(attributeName, attributeValue);
+
+ expect(firstSibling).toHaveAttribute(attributeName, attributeValue);
+ expect(thirdSibling).toHaveAttribute(attributeName, attributeValue);
+ expect(noSibling).not.toHaveAttribute(attributeName, attributeValue);
+ });
+
+ it("should remove attribute from all siblings when removeAttribute is called", () => {
+ plainRender();
+ const targetNode = screen.getByRole("region", { name: "Second sibling" });
+ const firstSibling = screen.getByRole("region", { name: "First sibling" });
+ const thirdSibling = screen.getByRole("region", { name: "Third sibling" });
+ const noSibling = screen.getByRole("region", { name: "Not a sibling" });
+ const { result } = renderHook(() => useNodeSiblings(targetNode));
+ const [, removeAttribute] = result.current;
+
+ expect(firstSibling).toHaveAttribute("data-foo", "bar");
+ expect(thirdSibling).toHaveAttribute("data-foo", "bar");
+ expect(noSibling).toHaveAttribute("data-foo", "bar");
+
+ removeAttribute("data-foo");
+
+ expect(firstSibling).not.toHaveAttribute("data-foo", "bar");
+ expect(thirdSibling).not.toHaveAttribute("data-foo", "bar");
+ expect(noSibling).toHaveAttribute("data-foo", "bar");
+ });
+});
diff --git a/web/src/hooks/useNodeSiblings.js b/web/src/hooks/useNodeSiblings.ts
similarity index 54%
rename from web/src/hooks/useNodeSiblings.js
rename to web/src/hooks/useNodeSiblings.ts
index cf98a42d5e..1b21cfc34e 100644
--- a/web/src/hooks/useNodeSiblings.js
+++ b/web/src/hooks/useNodeSiblings.ts
@@ -1,42 +1,26 @@
import { noop } from "~/utils";
-/**
- * Function for adding an attribute to a sibling
- *
- * @typedef {function} addAttributeFn
- * @param {string} attribute - attribute name
- * @param {*} value - value to set
- */
-
-/**
- * Function for removing an attribute from a sibling
- *
- * @typedef {function} removeAttributeFn
- * @param {string} attribute - attribute name
- */
-
/**
* A hook for working with siblings of the node passed as parameter
*
* It returns an array with exactly two functions:
* - First for adding given attribute to siblings
* - Second for removing given attributes from siblings
- *
- * @param {HTMLElement} node
- * @returns {[addAttributeFn, removeAttributeFn]}
*/
-const useNodeSiblings = (node) => {
+const useNodeSiblings = (
+ node: HTMLElement,
+): [(attribute: string, value) => void, (attribute: string) => void] => {
if (!node) return [noop, noop];
const siblings = [...node.parentNode.children].filter((n) => n !== node);
- const addAttribute = (attribute, value) => {
+ const addAttribute = (attribute: string, value) => {
siblings.forEach((sibling) => {
sibling.setAttribute(attribute, value);
});
};
- const removeAttribute = (attribute) => {
+ const removeAttribute = (attribute: string) => {
siblings.forEach((sibling) => {
sibling.removeAttribute(attribute);
});
From c1684a8115ebe3ddfc51f9a48cff0935f3509c79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Mon, 30 Dec 2024 14:33:31 +0000
Subject: [PATCH 057/147] refactor(web): migrate user utils to TypeScript
---
.../users/{utils.test.js => utils.test.ts} | 0
web/src/components/users/{utils.js => utils.ts} | 13 +++++++------
2 files changed, 7 insertions(+), 6 deletions(-)
rename web/src/components/users/{utils.test.js => utils.test.ts} (100%)
rename web/src/components/users/{utils.js => utils.ts} (86%)
diff --git a/web/src/components/users/utils.test.js b/web/src/components/users/utils.test.ts
similarity index 100%
rename from web/src/components/users/utils.test.js
rename to web/src/components/users/utils.test.ts
diff --git a/web/src/components/users/utils.js b/web/src/components/users/utils.ts
similarity index 86%
rename from web/src/components/users/utils.js
rename to web/src/components/users/utils.ts
index c1a9ec62aa..233ff584b5 100644
--- a/web/src/components/users/utils.js
+++ b/web/src/components/users/utils.ts
@@ -22,13 +22,14 @@
/**
* Method which generates username suggestions based on given full name.
- * The method cleans the input name by removing non-alphanumeric characters (except spaces),
+ *
+ * The method cleans given name by removing non-alphanumeric characters (except spaces),
* splits the name into parts, and then generates suggestions based on these parts.
*
- * @param {string} fullName The full name used to generate username suggestions.
- * @returns {string[]} An array of username suggestions.
+ * @param fullName The full name used to generate username suggestions.
+ * @returns An array of username suggestions.
*/
-const suggestUsernames = (fullName) => {
+const suggestUsernames = (fullName: string) => {
if (!fullName) return [];
// Cleaning the name.
@@ -41,7 +42,8 @@ const suggestUsernames = (fullName) => {
// Split the cleaned name into parts.
const parts = cleanedName.split(/\s+/);
- const suggestions = new Set();
+ // Uses Set for avoiding duplicates
+ const suggestions = new Set();
const firstLetters = parts.map((p) => p[0]).join("");
const lastPosition = parts.length - 1;
@@ -66,7 +68,6 @@ const suggestUsernames = (fullName) => {
if (s.length < 3) suggestions.delete(s);
});
- // using Set object to remove duplicates, then converting back to array
return [...suggestions];
};
From e97c575d79e970bdcec3193c5f11cd9376a38b9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 09:20:59 +0000
Subject: [PATCH 058/147] refactor(web): move index files to TypeScript
---
web/src/components/core/{index.js => index.ts} | 0
web/src/components/l10n/{index.js => index.ts} | 0
web/src/components/layout/{index.js => index.ts} | 0
web/src/components/network/{index.js => index.ts} | 0
web/src/components/overview/{index.js => index.ts} | 0
web/src/components/software/{index.js => index.ts} | 0
web/src/components/storage/dasd/{index.js => index.ts} | 0
web/src/components/storage/{index.js => index.ts} | 0
web/src/components/storage/iscsi/{index.js => index.ts} | 0
web/src/components/storage/zfcp/{index.js => index.ts} | 0
web/src/components/users/{index.js => index.ts} | 0
11 files changed, 0 insertions(+), 0 deletions(-)
rename web/src/components/core/{index.js => index.ts} (100%)
rename web/src/components/l10n/{index.js => index.ts} (100%)
rename web/src/components/layout/{index.js => index.ts} (100%)
rename web/src/components/network/{index.js => index.ts} (100%)
rename web/src/components/overview/{index.js => index.ts} (100%)
rename web/src/components/software/{index.js => index.ts} (100%)
rename web/src/components/storage/dasd/{index.js => index.ts} (100%)
rename web/src/components/storage/{index.js => index.ts} (100%)
rename web/src/components/storage/iscsi/{index.js => index.ts} (100%)
rename web/src/components/storage/zfcp/{index.js => index.ts} (100%)
rename web/src/components/users/{index.js => index.ts} (100%)
diff --git a/web/src/components/core/index.js b/web/src/components/core/index.ts
similarity index 100%
rename from web/src/components/core/index.js
rename to web/src/components/core/index.ts
diff --git a/web/src/components/l10n/index.js b/web/src/components/l10n/index.ts
similarity index 100%
rename from web/src/components/l10n/index.js
rename to web/src/components/l10n/index.ts
diff --git a/web/src/components/layout/index.js b/web/src/components/layout/index.ts
similarity index 100%
rename from web/src/components/layout/index.js
rename to web/src/components/layout/index.ts
diff --git a/web/src/components/network/index.js b/web/src/components/network/index.ts
similarity index 100%
rename from web/src/components/network/index.js
rename to web/src/components/network/index.ts
diff --git a/web/src/components/overview/index.js b/web/src/components/overview/index.ts
similarity index 100%
rename from web/src/components/overview/index.js
rename to web/src/components/overview/index.ts
diff --git a/web/src/components/software/index.js b/web/src/components/software/index.ts
similarity index 100%
rename from web/src/components/software/index.js
rename to web/src/components/software/index.ts
diff --git a/web/src/components/storage/dasd/index.js b/web/src/components/storage/dasd/index.ts
similarity index 100%
rename from web/src/components/storage/dasd/index.js
rename to web/src/components/storage/dasd/index.ts
diff --git a/web/src/components/storage/index.js b/web/src/components/storage/index.ts
similarity index 100%
rename from web/src/components/storage/index.js
rename to web/src/components/storage/index.ts
diff --git a/web/src/components/storage/iscsi/index.js b/web/src/components/storage/iscsi/index.ts
similarity index 100%
rename from web/src/components/storage/iscsi/index.js
rename to web/src/components/storage/iscsi/index.ts
diff --git a/web/src/components/storage/zfcp/index.js b/web/src/components/storage/zfcp/index.ts
similarity index 100%
rename from web/src/components/storage/zfcp/index.js
rename to web/src/components/storage/zfcp/index.ts
diff --git a/web/src/components/users/index.js b/web/src/components/users/index.ts
similarity index 100%
rename from web/src/components/users/index.js
rename to web/src/components/users/index.ts
From 62e1b04493dbbbb5256a855f5b7bc3d586f00f63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 11:50:51 +0000
Subject: [PATCH 059/147] refactor(web): migrate core/RowActions to TypeScript
---
web/src/components/core/RowActions.test.tsx | 71 +++++++++++++++++++
.../core/{RowActions.jsx => RowActions.tsx} | 32 +++++----
2 files changed, 90 insertions(+), 13 deletions(-)
create mode 100644 web/src/components/core/RowActions.test.tsx
rename web/src/components/core/{RowActions.jsx => RowActions.tsx} (77%)
diff --git a/web/src/components/core/RowActions.test.tsx b/web/src/components/core/RowActions.test.tsx
new file mode 100644
index 0000000000..25c461d5db
--- /dev/null
+++ b/web/src/components/core/RowActions.test.tsx
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) [2024] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+import React from "react";
+import { IAction } from "@patternfly/react-table";
+import { screen } from "@testing-library/react";
+import { plainRender } from "~/test-utils";
+import { RowActions } from "~/components/core";
+import { Icon } from "../layout";
+import { _ } from "~/i18n";
+
+const mockEditFn = jest.fn();
+const mockDeleteFn = jest.fn();
+
+const actions: IAction[] = [
+ {
+ title: _("Edit"),
+ role: "link",
+ "aria-label": _("Dummy edit action"),
+ onClick: mockEditFn,
+ },
+ {
+ title: _("Delete"),
+ "aria-label": _("Dummy delete action"),
+ icon: ,
+ onClick: mockDeleteFn,
+ isDanger: true,
+ },
+];
+
+describe("RowActions", () => {
+ it("allows interacting with given actions from a dropdown menu", async () => {
+ const { user } = plainRender(
+ ,
+ );
+
+ const button = screen.getByRole("button", { name: "Actions for testing" });
+ await user.click(button);
+ screen.getByRole("menu");
+ const editAction = screen.getByRole("menuitem", { name: "Dummy edit action" });
+ await user.click(editAction);
+ expect(mockEditFn).toHaveBeenCalled();
+ await user.click(button);
+ const deleteAction = screen.getByRole("menuitem", { name: "Dummy delete action" });
+ await user.click(deleteAction);
+ expect(mockDeleteFn).toHaveBeenCalled();
+ });
+});
diff --git a/web/src/components/core/RowActions.jsx b/web/src/components/core/RowActions.tsx
similarity index 77%
rename from web/src/components/core/RowActions.jsx
rename to web/src/components/core/RowActions.tsx
index 8a6321bdc4..27f2be1cd7 100644
--- a/web/src/components/core/RowActions.jsx
+++ b/web/src/components/core/RowActions.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -22,13 +22,21 @@
import React from "react";
import { MenuToggle } from "@patternfly/react-core";
-import { ActionsColumn } from "@patternfly/react-table";
-
+import {
+ ActionsColumn,
+ ActionsColumnProps,
+ CustomActionsToggleProps,
+} from "@patternfly/react-table";
import { Icon } from "~/components/layout";
import { _ } from "~/i18n";
+type RowActionsProps = {
+ id: string;
+ actions: ActionsColumnProps["items"];
+} & Omit;
+
/**
- * Renders icon for selecting the options of a row in a table
+ * Renders available options for a row in a table
* @component
*
* @example
@@ -46,16 +54,14 @@ import { _ } from "~/i18n";
* }
* ]}
* />
- *
- * @param {object} props
- * @param {string} props.id
- * @param {Action[]} props.actions
- * @param {object} [props.rest]
- *
- * @typedef {import("@patternfly/react-table").IAction} Action
*/
-export default function RowActions({ id, actions, "aria-label": toggleAriaLabel, ...rest }) {
- const actionsToggle = (props) => (
+export default function RowActions({
+ id,
+ actions,
+ "aria-label": toggleAriaLabel,
+ ...rest
+}: RowActionsProps) {
+ const actionsToggle = (props: CustomActionsToggleProps) => (
Date: Thu, 2 Jan 2025 12:01:45 +0000
Subject: [PATCH 060/147] refactor(web): migrate core/NumericTextInput to
TypeScript
---
...put.test.jsx => NumericTextInput.test.tsx} | 4 +--
...ericTextInput.jsx => NumericTextInput.tsx} | 30 ++++++++-----------
2 files changed, 14 insertions(+), 20 deletions(-)
rename web/src/components/core/{NumericTextInput.test.jsx => NumericTextInput.test.tsx} (95%)
rename web/src/components/core/{NumericTextInput.jsx => NumericTextInput.tsx} (66%)
diff --git a/web/src/components/core/NumericTextInput.test.jsx b/web/src/components/core/NumericTextInput.test.tsx
similarity index 95%
rename from web/src/components/core/NumericTextInput.test.jsx
rename to web/src/components/core/NumericTextInput.test.tsx
index 2078a24e5c..760aac843e 100644
--- a/web/src/components/core/NumericTextInput.test.jsx
+++ b/web/src/components/core/NumericTextInput.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022-2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -29,7 +29,7 @@ import { NumericTextInput } from "~/components/core";
// the given onChange callback is called. The former is more aligned with the
// React Testing Library principles, https://testing-library.com/docs/guiding-principles
const Input = ({ value: initialValue = "" }) => {
- const [value, setValue] = useState(initialValue);
+ const [value, setValue] = useState(initialValue);
return ;
};
diff --git a/web/src/components/core/NumericTextInput.jsx b/web/src/components/core/NumericTextInput.tsx
similarity index 66%
rename from web/src/components/core/NumericTextInput.jsx
rename to web/src/components/core/NumericTextInput.tsx
index 1ecd2aa984..50302af7b2 100644
--- a/web/src/components/core/NumericTextInput.jsx
+++ b/web/src/components/core/NumericTextInput.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -20,19 +20,14 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React from "react";
-import { TextInput } from "@patternfly/react-core";
+import { TextInput, TextInputProps } from "@patternfly/react-core";
import { noop } from "~/utils";
-/**
- * Callback function for notifying a valid input change
- *
- * @callback onChangeFn
- * @param {string|number} the input value
- * @return {void}
- */
+type NumericTextInputProps = {
+ value: string | number;
+ onChange: (value: string | number) => void;
+} & Omit;
/**
* Helper component for having an input text limited to not signed numbers
@@ -41,17 +36,16 @@ import { noop } from "~/utils";
* Based on {@link https://www.patternfly.org/components/forms/text-input PF/TextInput}
*
* @note It allows empty value too.
- *
- * @param {object} props
- * @param {string|number} props.value - the input value
- * @param {onChangeFn} props.onChange - the callback to be called when the entered value match the input pattern
- * @param {import("@patternfly/react-core").TextInputProps} props.textInputProps
*/
-export default function NumericTextInput({ value = "", onChange = noop, ...textInputProps }) {
+export default function NumericTextInput({
+ value = "",
+ onChange = noop,
+ ...textInputProps
+}: NumericTextInputProps) {
// NOTE: Using \d* instead of \d+ at the beginning to allow empty
const pattern = /^\d*\.?\d*$/;
- const handleOnChange = (_, value) => {
+ const handleOnChange: TextInputProps["onChange"] = (_, value) => {
if (pattern.test(value)) {
onChange(value);
}
From b01c27ea43603fbfd3c47efe30b715ae627a26dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 12:09:26 +0000
Subject: [PATCH 061/147] fix(web): please ESLint after migrating
NumericTextInput
---
web/src/components/storage/VolumeFields.tsx | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/web/src/components/storage/VolumeFields.tsx b/web/src/components/storage/VolumeFields.tsx
index 3d5beb6a0a..aafe93e25b 100644
--- a/web/src/components/storage/VolumeFields.tsx
+++ b/web/src/components/storage/VolumeFields.tsx
@@ -344,7 +344,6 @@ const SizeManual = ({
onChange({ minSize })}
- validated={errors.minSize && "error"}
+ validated={errors.minSize ? "error" : "default"}
isDisabled={isDisabled}
/>
@@ -419,14 +418,13 @@ and maximum. If no maximum is given then the file system will be as big as possi
onChange({ minSize })}
- validated={errors.minSize && "error"}
+ validated={errors.minSize ? "error" : "default"}
isDisabled={isDisabled}
/>
@@ -453,10 +451,9 @@ and maximum. If no maximum is given then the file system will be as big as possi
Date: Thu, 2 Jan 2025 13:15:06 +0000
Subject: [PATCH 062/147] refactor(web): migrate core/ListSearch to TypeScript
---
...istSearch.test.jsx => ListSearch.test.tsx} | 2 +-
.../core/{ListSearch.jsx => ListSearch.tsx} | 31 ++++++++++---------
2 files changed, 18 insertions(+), 15 deletions(-)
rename web/src/components/core/{ListSearch.test.jsx => ListSearch.test.tsx} (98%)
rename web/src/components/core/{ListSearch.jsx => ListSearch.tsx} (76%)
diff --git a/web/src/components/core/ListSearch.test.jsx b/web/src/components/core/ListSearch.test.tsx
similarity index 98%
rename from web/src/components/core/ListSearch.test.jsx
rename to web/src/components/core/ListSearch.test.tsx
index 1baf97cfea..d29e368dfb 100644
--- a/web/src/components/core/ListSearch.test.jsx
+++ b/web/src/components/core/ListSearch.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2024] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/components/core/ListSearch.jsx b/web/src/components/core/ListSearch.tsx
similarity index 76%
rename from web/src/components/core/ListSearch.jsx
rename to web/src/components/core/ListSearch.tsx
index 1b3467f1f0..780feb574b 100644
--- a/web/src/components/core/ListSearch.jsx
+++ b/web/src/components/core/ListSearch.tsx
@@ -25,44 +25,47 @@ import { SearchInput } from "@patternfly/react-core";
import { _ } from "~/i18n";
import { noop, useDebounce } from "~/utils";
-const search = (elements, term) => {
+type ListSearchProps = {
+ /** Text to display as placeholder for the search input. */
+ placeholder?: string;
+ /** List of elements in which to search. */
+ elements: T[];
+ /** Callback to be called with the filtered list of elements. */
+ onChange: (elements: T[]) => void;
+};
+
+function search(elements: T[], term: string): T[] {
const value = term.toLowerCase();
- const match = (element) => {
+ const match = (element: T) => {
return Object.values(element).join("").toLowerCase().includes(value);
};
return elements.filter(match);
-};
+}
/**
- * TODO: Rename and/or refactor?
* Input field for searching in a given list of elements.
* @component
- *
- * @param {object} props
- * @param {string} [props.placeholder]
- * @param {object[]} [props.elements] - List of elements in which to search.
- * @param {(elements: object[]) => void} [props.onChange] - Callback to be called with the filtered list of elements.
*/
-export default function ListSearch({
+export default function ListSearch({
placeholder = _("Search"),
elements = [],
onChange: onChangeProp = noop,
-}) {
+}: ListSearchProps) {
const [value, setValue] = useState("");
const [resultSize, setResultSize] = useState(elements.length);
- const updateResult = (result) => {
+ const updateResult = (result: T[]) => {
setResultSize(result.length);
onChangeProp(result);
};
- const searchHandler = useDebounce((term) => {
+ const searchHandler = useDebounce((term: string) => {
updateResult(search(elements, term));
}, 500);
- const onChange = (value) => {
+ const onChange = (value: string) => {
setValue(value);
searchHandler(value);
};
From 7ec3d978fc7d01c3a756368f29ec5c610c4d7aa5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 13:17:45 +0000
Subject: [PATCH 063/147] refactor(web): drop core/SectionSkeleton
Because it's no longer use.
---
web/src/components/core/SectionSkeleton.jsx | 42 ---------------------
web/src/components/core/index.ts | 1 -
2 files changed, 43 deletions(-)
delete mode 100644 web/src/components/core/SectionSkeleton.jsx
diff --git a/web/src/components/core/SectionSkeleton.jsx b/web/src/components/core/SectionSkeleton.jsx
deleted file mode 100644
index 59337e133b..0000000000
--- a/web/src/components/core/SectionSkeleton.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) [2022-2023] SUSE LLC
- *
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, contact SUSE LLC.
- *
- * To contact SUSE LLC about this file by physical or electronic mail, you may
- * find current contact information at www.suse.com.
- */
-
-import React from "react";
-import { Skeleton } from "@patternfly/react-core";
-import { _ } from "~/i18n";
-
-const WaitingSkeleton = ({ width }) => {
- return ;
-};
-
-const SectionSkeleton = ({ numRows = 2 }) => {
- return (
- <>
- {Array.from({ length: numRows }, (_, i) => {
- const width = i % 2 === 0 ? "50%" : "25%";
- return ;
- })}
- >
- );
-};
-
-export default SectionSkeleton;
diff --git a/web/src/components/core/index.ts b/web/src/components/core/index.ts
index 0345080fd4..dd18a31925 100644
--- a/web/src/components/core/index.ts
+++ b/web/src/components/core/index.ts
@@ -32,7 +32,6 @@ export { default as InstallationFinished } from "./InstallationFinished";
export { default as InstallationProgress } from "./InstallationProgress";
export { default as InstallButton } from "./InstallButton";
export { default as IssuesHint } from "./IssuesHint";
-export { default as SectionSkeleton } from "./SectionSkeleton";
export { default as ListSearch } from "./ListSearch";
export { default as LoginPage } from "./LoginPage";
export { default as RowActions } from "./RowActions";
From dfa51aae7ed60a7e6b804ca8ae0ca9fef1bf8982 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 13:20:17 +0000
Subject: [PATCH 064/147] refactor(web): migratre layout/Center to TypeScript
---
web/src/components/layout/{Center.jsx => Center.tsx} | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
rename web/src/components/layout/{Center.jsx => Center.tsx} (91%)
diff --git a/web/src/components/layout/Center.jsx b/web/src/components/layout/Center.tsx
similarity index 91%
rename from web/src/components/layout/Center.jsx
rename to web/src/components/layout/Center.tsx
index 2f3046ec3a..48c4a53512 100644
--- a/web/src/components/layout/Center.jsx
+++ b/web/src/components/layout/Center.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022] SUSE LLC
+ * Copyright (c) [2022-2024] SUSE LLC
*
* All Rights Reserved.
*
@@ -20,8 +20,6 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React from "react";
/**
@@ -44,11 +42,8 @@ import React from "react";
* To know more, read
* - https://www.w3.org/TR/selectors-4/#relational
* - https://ishadeed.com/article/css-has-parent-selector/
- *
- * @param {object} props
- * @param {React.ReactNode} props.children
*/
-const Center = ({ children }) => (
+const Center = ({ children }: React.PropsWithChildren) => (
{children}
From 8876cf528a9a8d7961897d3ee044b258bc4fe168 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 13:23:03 +0000
Subject: [PATCH 065/147] refactor(web): migrate to core/IssuesHint to
TypeScript
---
.../core/{IssuesHint.test.jsx => IssuesHint.test.tsx} | 2 +-
web/src/components/core/{IssuesHint.jsx => IssuesHint.tsx} | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
rename web/src/components/core/{IssuesHint.test.jsx => IssuesHint.test.tsx} (97%)
rename web/src/components/core/{IssuesHint.jsx => IssuesHint.tsx} (91%)
diff --git a/web/src/components/core/IssuesHint.test.jsx b/web/src/components/core/IssuesHint.test.tsx
similarity index 97%
rename from web/src/components/core/IssuesHint.test.jsx
rename to web/src/components/core/IssuesHint.test.tsx
index a4a0d5a19e..3b654a88f4 100644
--- a/web/src/components/core/IssuesHint.test.jsx
+++ b/web/src/components/core/IssuesHint.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022-2023] SUSE LLC
+ * Copyright (c) [2022-2025] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/components/core/IssuesHint.jsx b/web/src/components/core/IssuesHint.tsx
similarity index 91%
rename from web/src/components/core/IssuesHint.jsx
rename to web/src/components/core/IssuesHint.tsx
index 9a9c92a6d9..86ee00cad7 100644
--- a/web/src/components/core/IssuesHint.jsx
+++ b/web/src/components/core/IssuesHint.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -23,6 +23,7 @@
import React from "react";
import { Hint, HintBody, List, ListItem, Stack } from "@patternfly/react-core";
import { _ } from "~/i18n";
+import { Issue } from "~/types/issues";
export default function IssuesHint({ issues }) {
if (issues === undefined || issues.length === 0) return;
@@ -35,7 +36,7 @@ export default function IssuesHint({ issues }) {
{_("Before starting the installation, you need to address the following problems:")}
- {issues.map((i, idx) => (
+ {issues.map((i: Issue, idx: number) => (
{i.description}
))}
From 39bdcb68f5688b895d4395af58682f52df33d0a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 13:26:12 +0000
Subject: [PATCH 066/147] refactor(web): migrate L10n page and sections to
TypeScript
---
.../components/l10n/{L10nPage.test.jsx => L10nPage.test.tsx} | 2 +-
web/src/components/l10n/{L10nPage.jsx => L10nPage.tsx} | 2 +-
.../overview/{L10nSection.test.jsx => L10nSection.test.tsx} | 2 +-
.../components/overview/{L10nSection.jsx => L10nSection.tsx} | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
rename web/src/components/l10n/{L10nPage.test.jsx => L10nPage.test.tsx} (98%)
rename web/src/components/l10n/{L10nPage.jsx => L10nPage.tsx} (98%)
rename web/src/components/overview/{L10nSection.test.jsx => L10nSection.test.tsx} (97%)
rename web/src/components/overview/{L10nSection.jsx => L10nSection.tsx} (97%)
diff --git a/web/src/components/l10n/L10nPage.test.jsx b/web/src/components/l10n/L10nPage.test.tsx
similarity index 98%
rename from web/src/components/l10n/L10nPage.test.jsx
rename to web/src/components/l10n/L10nPage.test.tsx
index 7229c51903..48f26d6255 100644
--- a/web/src/components/l10n/L10nPage.test.jsx
+++ b/web/src/components/l10n/L10nPage.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022-2024] SUSE LLC
+ * Copyright (c) [2022-2025] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/components/l10n/L10nPage.jsx b/web/src/components/l10n/L10nPage.tsx
similarity index 98%
rename from web/src/components/l10n/L10nPage.jsx
rename to web/src/components/l10n/L10nPage.tsx
index 5ead5d01f6..4e0121f788 100644
--- a/web/src/components/l10n/L10nPage.jsx
+++ b/web/src/components/l10n/L10nPage.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022-2023] SUSE LLC
+ * Copyright (c) [2022-2025] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/components/overview/L10nSection.test.jsx b/web/src/components/overview/L10nSection.test.tsx
similarity index 97%
rename from web/src/components/overview/L10nSection.test.jsx
rename to web/src/components/overview/L10nSection.test.tsx
index c89155fd5a..efb9a41ff0 100644
--- a/web/src/components/overview/L10nSection.test.jsx
+++ b/web/src/components/overview/L10nSection.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/components/overview/L10nSection.jsx b/web/src/components/overview/L10nSection.tsx
similarity index 97%
rename from web/src/components/overview/L10nSection.jsx
rename to web/src/components/overview/L10nSection.tsx
index a82d72cb8d..37308e410d 100644
--- a/web/src/components/overview/L10nSection.jsx
+++ b/web/src/components/overview/L10nSection.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
From 0bb51fd248051ad8495db7f73f525d02c191368c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 13:28:57 +0000
Subject: [PATCH 067/147] refactor(web): migrate core/ProgressText to
TypeScript
---
...essText.test.jsx => ProgressText.test.tsx} | 2 +-
.../{ProgressText.jsx => ProgressText.tsx} | 22 +++++++++----------
2 files changed, 12 insertions(+), 12 deletions(-)
rename web/src/components/core/{ProgressText.test.jsx => ProgressText.test.tsx} (97%)
rename web/src/components/core/{ProgressText.jsx => ProgressText.tsx} (82%)
diff --git a/web/src/components/core/ProgressText.test.jsx b/web/src/components/core/ProgressText.test.tsx
similarity index 97%
rename from web/src/components/core/ProgressText.test.jsx
rename to web/src/components/core/ProgressText.test.tsx
index 0c7e49034b..8fc5f21206 100644
--- a/web/src/components/core/ProgressText.test.jsx
+++ b/web/src/components/core/ProgressText.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
diff --git a/web/src/components/core/ProgressText.jsx b/web/src/components/core/ProgressText.tsx
similarity index 82%
rename from web/src/components/core/ProgressText.jsx
rename to web/src/components/core/ProgressText.tsx
index 5ac7dcac57..8a31346e2a 100644
--- a/web/src/components/core/ProgressText.jsx
+++ b/web/src/components/core/ProgressText.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -20,22 +20,22 @@
* find current contact information at www.suse.com.
*/
-// @ts-check
-
import React from "react";
import { Split, Text } from "@patternfly/react-core";
+type ProgressTextProps = {
+ /** Progress message. */
+ message: string;
+ /** Current step. */
+ current: number;
+ /** Total steps. */
+ total: number;
+};
+
/**
* Progress description
- *
- * @component
- *
- * @param {object} props
- * @param {string} [props.message] Progress message
- * @param {number} [props.current] Current step
- * @param {number} [props.total] Number of steps
*/
-export default function ProgressText({ message, current, total }) {
+export default function ProgressText({ message, current, total }: ProgressTextProps) {
const text = current === 0 ? message : `${message} (${current}/${total})`;
return (
From d6815ceb8a49dffd8ee19decb35ff3a2e0a36c13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Thu, 2 Jan 2025 14:06:23 +0000
Subject: [PATCH 068/147] refactor(web): migrate ProductSelectionProgress to
TypeScript
---
.../product/ProductSelectionProgress.test.tsx | 63 +++++++++++++++++++
...gress.jsx => ProductSelectionProgress.tsx} | 4 +-
2 files changed, 64 insertions(+), 3 deletions(-)
create mode 100644 web/src/components/product/ProductSelectionProgress.test.tsx
rename web/src/components/product/{ProductSelectionProgress.jsx => ProductSelectionProgress.tsx} (96%)
diff --git a/web/src/components/product/ProductSelectionProgress.test.tsx b/web/src/components/product/ProductSelectionProgress.test.tsx
new file mode 100644
index 0000000000..082ac388d2
--- /dev/null
+++ b/web/src/components/product/ProductSelectionProgress.test.tsx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) [2025] SUSE LLC
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, contact SUSE LLC.
+ *
+ * To contact SUSE LLC about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+import React from "react";
+import { screen } from "@testing-library/react";
+import { installerRender } from "~/test-utils";
+import ProductSelectionProgress from "./ProductSelectionProgress";
+import { ROOT } from "~/routes/paths";
+import { Product } from "~/types/software";
+
+jest.mock("~/components/core/ProgressReport", () => () =>
ProgressReport Mock
);
+
+let isBusy = false;
+const tumbleweed: Product = { id: "openSUSE", name: "openSUSE Tumbleweed" };
+
+jest.mock("~/queries/status", () => ({
+ ...jest.requireActual("~/queries/status"),
+ useInstallerStatus: () => ({ isBusy }),
+}));
+
+jest.mock("~/queries/software", () => ({
+ ...jest.requireActual("~/queries/software"),
+ useProduct: () => ({ selectedProduct: tumbleweed }),
+}));
+
+describe("ProductSelectionProgress", () => {
+ describe("when installer is not busy", () => {
+ it("redirects to the root path", async () => {
+ installerRender();
+ await screen.findByText(`Navigating to ${ROOT.root}`);
+ });
+ });
+
+ describe("when installer in busy", () => {
+ beforeEach(() => {
+ isBusy = true;
+ });
+
+ it("renders progress report", () => {
+ installerRender();
+ screen.getByText("ProgressReport Mock");
+ });
+ });
+});
diff --git a/web/src/components/product/ProductSelectionProgress.jsx b/web/src/components/product/ProductSelectionProgress.tsx
similarity index 96%
rename from web/src/components/product/ProductSelectionProgress.jsx
rename to web/src/components/product/ProductSelectionProgress.tsx
index cfc80bed82..09df5286d2 100644
--- a/web/src/components/product/ProductSelectionProgress.jsx
+++ b/web/src/components/product/ProductSelectionProgress.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2024] SUSE LLC
+ * Copyright (c) [2024-2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -29,8 +29,6 @@ import { ROOT as PATHS } from "~/routes/paths";
import { _ } from "~/i18n";
/**
- * @component
- *
* Shows progress steps when a product is selected.
*/
function ProductSelectionProgress() {
From 5a95f481b9bbed2d0ff0162f45f144d8a7005b3a Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Fri, 3 Jan 2025 09:33:44 +0100
Subject: [PATCH 069/147] migrate answers example from yaml to json
---
doc/answers_example.json | 6 ++++++
doc/answers_example.yaml | 3 ---
2 files changed, 6 insertions(+), 3 deletions(-)
create mode 100644 doc/answers_example.json
delete mode 100644 doc/answers_example.yaml
diff --git a/doc/answers_example.json b/doc/answers_example.json
new file mode 100644
index 0000000000..a82f580064
--- /dev/null
+++ b/doc/answers_example.json
@@ -0,0 +1,6 @@
+{ "answers": [{
+ "class": "storage.luks_activation",
+ "answer": "decrypt",
+ "password": "my_password"
+ }]
+}
diff --git a/doc/answers_example.yaml b/doc/answers_example.yaml
deleted file mode 100644
index 581f39ff1d..0000000000
--- a/doc/answers_example.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-answers:
- - class: storage.luks_activation
- answer: "skip"
From b1f89d38d71d5bf3972268c934037ec188adfe5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 3 Jan 2025 11:48:33 +0000
Subject: [PATCH 070/147] fix(web): improve useNodeSiblings type readbility
---
web/src/hooks/useNodeSiblings.ts | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/web/src/hooks/useNodeSiblings.ts b/web/src/hooks/useNodeSiblings.ts
index 1b21cfc34e..d6418c0979 100644
--- a/web/src/hooks/useNodeSiblings.ts
+++ b/web/src/hooks/useNodeSiblings.ts
@@ -1,5 +1,8 @@
import { noop } from "~/utils";
+type AddAttributeFn = HTMLElement["setAttribute"];
+type RemoveAttributeFn = HTMLElement["removeAttribute"];
+
/**
* A hook for working with siblings of the node passed as parameter
*
@@ -7,20 +10,18 @@ import { noop } from "~/utils";
* - First for adding given attribute to siblings
* - Second for removing given attributes from siblings
*/
-const useNodeSiblings = (
- node: HTMLElement,
-): [(attribute: string, value) => void, (attribute: string) => void] => {
+const useNodeSiblings = (node: HTMLElement): [AddAttributeFn, RemoveAttributeFn] => {
if (!node) return [noop, noop];
const siblings = [...node.parentNode.children].filter((n) => n !== node);
- const addAttribute = (attribute: string, value) => {
+ const addAttribute: AddAttributeFn = (attribute, value) => {
siblings.forEach((sibling) => {
sibling.setAttribute(attribute, value);
});
};
- const removeAttribute = (attribute: string) => {
+ const removeAttribute: RemoveAttributeFn = (attribute: string) => {
siblings.forEach((sibling) => {
sibling.removeAttribute(attribute);
});
From 6d803cca3f37aec654b7b929ffe6d26f7b5453c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 3 Jan 2025 11:49:27 +0000
Subject: [PATCH 071/147] fix(web): stop using translations in test files
---
web/src/components/core/Page.test.tsx | 31 +++++++++----------
.../components/core/PasswordInput.test.tsx | 9 +++---
web/src/components/core/RowActions.test.tsx | 11 +++----
.../storage/DevicesTechMenu.test.tsx | 13 ++++----
4 files changed, 30 insertions(+), 34 deletions(-)
diff --git a/web/src/components/core/Page.test.tsx b/web/src/components/core/Page.test.tsx
index be2515d2a3..20f3393fde 100644
--- a/web/src/components/core/Page.test.tsx
+++ b/web/src/components/core/Page.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023-2024] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -24,7 +24,6 @@ import React from "react";
import { screen, within } from "@testing-library/react";
import { plainRender, mockNavigateFn } from "~/test-utils";
import { Page } from "~/components/core";
-import { _ } from "~/i18n";
let consoleErrorSpy: jest.SpyInstance;
@@ -41,7 +40,7 @@ describe("Page", () => {
it("renders given children", () => {
plainRender(
-
{_("The Page Component")}
+
The Page Component
,
);
screen.getByRole("heading", { name: "The Page Component" });
@@ -93,7 +92,7 @@ describe("Page", () => {
describe("Page.Content", () => {
it("renders a node that fills all the available space", () => {
- plainRender({_("The Content")});
+ plainRender(The Content);
const content = screen.getByText("The Content");
expect(content.classList.contains("pf-m-fill")).toBe(true);
});
@@ -149,7 +148,7 @@ describe("Page", () => {
});
describe("Page.Header", () => {
it("renders a node that sticks to top", () => {
- plainRender({_("The Header")});
+ plainRender(The Header);
const content = screen.getByText("The Header");
const container = content.parentNode as HTMLElement;
expect(container.classList.contains("pf-m-sticky-top")).toBe(true);
@@ -158,19 +157,19 @@ describe("Page", () => {
describe("Page.Section", () => {
it("outputs to console.error if both are missing, title and aria-label", () => {
- plainRender({_("Content")});
+ plainRender(Content);
expect(console.error).toHaveBeenCalledWith(expect.stringContaining("must have either"));
});
it("renders a section node", () => {
- plainRender({_("The Content")});
+ plainRender(The Content);
const section = screen.getByRole("region");
within(section).getByText("The Content");
});
it("adds the aria-labelledby attribute when title is given but aria-label is not", () => {
const { rerender } = plainRender(
- {_("The Content")},
+ The Content,
);
const section = screen.getByRole("region");
expect(section).toHaveAttribute("aria-labelledby");
@@ -178,7 +177,7 @@ describe("Page", () => {
// aria-label is given through Page.Section props
rerender(
- {_("The Content")}
+ The Content
,
);
expect(section).not.toHaveAttribute("aria-labelledby");
@@ -186,25 +185,25 @@ describe("Page", () => {
// aria-label is given through pfCardProps
rerender(
- {_("The Content")}
+ The Content
,
);
expect(section).not.toHaveAttribute("aria-labelledby");
// None was given, title nor aria-label
- rerender({_("The Content")});
+ rerender(The Content);
expect(section).not.toHaveAttribute("aria-labelledby");
});
it("renders given content props (title, value, description, actions, and children (content)", () => {
plainRender(
{_("Disable")}}
+ title="A section"
+ value="Enabled"
+ description="Testing section with title, value, description, content, and actions"
+ actions={Disable}
>
- {_("The Content")}
+ The Content
,
);
const section = screen.getByRole("region");
diff --git a/web/src/components/core/PasswordInput.test.tsx b/web/src/components/core/PasswordInput.test.tsx
index 8437498107..ad4f5aca27 100644
--- a/web/src/components/core/PasswordInput.test.tsx
+++ b/web/src/components/core/PasswordInput.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023-2024] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -25,18 +25,17 @@ import { screen } from "@testing-library/react";
import { plainRender } from "~/test-utils";
import userEvent from "@testing-library/user-event";
import PasswordInput, { PasswordInputProps } from "./PasswordInput";
-import { _ } from "~/i18n";
describe("PasswordInput Component", () => {
it("renders a password input", () => {
- plainRender();
+ plainRender();
const inputField = screen.getByLabelText("User password");
expect(inputField).toHaveAttribute("type", "password");
});
it("allows revealing the password", async () => {
- plainRender();
+ plainRender();
const passwordInput = screen.getByLabelText("User password");
const button = screen.getByRole("button");
@@ -48,7 +47,7 @@ describe("PasswordInput Component", () => {
it("applies autoFocus behavior correctly", () => {
plainRender(
- ,
+ ,
);
const inputField = screen.getByLabelText("User password");
diff --git a/web/src/components/core/RowActions.test.tsx b/web/src/components/core/RowActions.test.tsx
index 25c461d5db..9f5fc525da 100644
--- a/web/src/components/core/RowActions.test.tsx
+++ b/web/src/components/core/RowActions.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2024] SUSE LLC
+ * Copyright (c) [2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -26,21 +26,20 @@ import { screen } from "@testing-library/react";
import { plainRender } from "~/test-utils";
import { RowActions } from "~/components/core";
import { Icon } from "../layout";
-import { _ } from "~/i18n";
const mockEditFn = jest.fn();
const mockDeleteFn = jest.fn();
const actions: IAction[] = [
{
- title: _("Edit"),
+ title: "Edit",
role: "link",
- "aria-label": _("Dummy edit action"),
+ "aria-label": "Dummy edit action",
onClick: mockEditFn,
},
{
- title: _("Delete"),
- "aria-label": _("Dummy delete action"),
+ title: "Delete",
+ "aria-label": "Dummy delete action",
icon: ,
onClick: mockDeleteFn,
isDanger: true,
diff --git a/web/src/components/storage/DevicesTechMenu.test.tsx b/web/src/components/storage/DevicesTechMenu.test.tsx
index a527322d8a..5b8bf07358 100644
--- a/web/src/components/storage/DevicesTechMenu.test.tsx
+++ b/web/src/components/storage/DevicesTechMenu.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2023] SUSE LLC
+ * Copyright (c) [2023-2025] SUSE LLC
*
* All Rights Reserved.
*
@@ -24,7 +24,6 @@ import React from "react";
import { screen } from "@testing-library/react";
import { installerRender } from "~/test-utils";
import DevicesTechMenu from "./DevicesTechMenu";
-import { _ } from "~/i18n";
import { supportedDASD } from "~/api/storage/dasd";
import { supportedZFCP } from "~/api/storage/zfcp";
@@ -37,7 +36,7 @@ beforeEach(() => {
});
it("contains an entry for configuring iSCSI", async () => {
- const { user } = installerRender();
+ const { user } = installerRender();
const toggler = screen.getByRole("button");
await user.click(toggler);
const link = screen.getByRole("option", { name: /iSCSI/ });
@@ -45,7 +44,7 @@ it("contains an entry for configuring iSCSI", async () => {
});
it("does not contain an entry for configuring DASD when is NOT supported", async () => {
- const { user } = installerRender();
+ const { user } = installerRender();
const toggler = screen.getByRole("button");
await user.click(toggler);
expect(screen.queryByRole("option", { name: /DASD/ })).toBeNull();
@@ -53,7 +52,7 @@ it("does not contain an entry for configuring DASD when is NOT supported", async
it("contains an entry for configuring DASD when is supported", async () => {
(supportedDASD as jest.Mock).mockResolvedValue(true);
- const { user } = installerRender();
+ const { user } = installerRender();
const toggler = screen.getByRole("button");
await user.click(toggler);
const link = screen.getByRole("option", { name: /DASD/ });
@@ -61,7 +60,7 @@ it("contains an entry for configuring DASD when is supported", async () => {
});
it("does not contain an entry for configuring zFCP when is NOT supported", async () => {
- const { user } = installerRender();
+ const { user } = installerRender();
const toggler = screen.getByRole("button");
await user.click(toggler);
expect(screen.queryByRole("option", { name: /DASD/ })).toBeNull();
@@ -69,7 +68,7 @@ it("does not contain an entry for configuring zFCP when is NOT supported", async
it("contains an entry for configuring zFCP when is supported", async () => {
(supportedZFCP as jest.Mock).mockResolvedValue(true);
- const { user } = installerRender();
+ const { user } = installerRender();
const toggler = screen.getByRole("button");
await user.click(toggler);
const link = screen.getByRole("option", { name: /zFCP/ });
From 6ecc3161fca9fa0c25800f33ab27d72ca6675e7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Fri, 3 Jan 2025 11:51:17 +0000
Subject: [PATCH 072/147] fix(web): revert copyright miss-updated
---
web/src/components/core/IssuesHint.test.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/src/components/core/IssuesHint.test.tsx b/web/src/components/core/IssuesHint.test.tsx
index 3b654a88f4..6bead73a40 100644
--- a/web/src/components/core/IssuesHint.test.tsx
+++ b/web/src/components/core/IssuesHint.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) [2022-2025] SUSE LLC
+ * Copyright (c) [2022-2024] SUSE LLC
*
* All Rights Reserved.
*
From 428ecf4b0eb340f52b36b69ac639fe1a84bea814 Mon Sep 17 00:00:00 2001
From: YaST Bot
Date: Sun, 5 Jan 2025 03:07:18 +0000
Subject: [PATCH 073/147] Update web translation files
Agama-weblate commit: 49c2ce54826ea6d04ce8fb806a4bd9fbe1a5a8a2
---
web/src/po/po.ca.js | 3 -
web/src/po/po.cs.js | 3 -
web/src/po/po.de.js | 3 -
web/src/po/po.es.js | 3 -
web/src/po/po.fr.js | 3 -
web/src/po/po.id.js | 293 ++++++++++++++++++++++++++++++++++++++-
web/src/po/po.ja.js | 3 -
web/src/po/po.nb_NO.js | 3 -
web/src/po/po.pt_BR.js | 3 -
web/src/po/po.ru.js | 3 -
web/src/po/po.sv.js | 3 -
web/src/po/po.tr.js | 3 -
web/src/po/po.zh_Hans.js | 3 -
13 files changed, 286 insertions(+), 43 deletions(-)
diff --git a/web/src/po/po.ca.js b/web/src/po/po.ca.js
index cc98043e22..236defff88 100644
--- a/web/src/po/po.ca.js
+++ b/web/src/po/po.ca.js
@@ -1390,9 +1390,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Escrivint"
- ],
"Waiting for actions information...": [
"Esperant la informació de les accions..."
],
diff --git a/web/src/po/po.cs.js b/web/src/po/po.cs.js
index dc9cb32ecd..b267118fcb 100644
--- a/web/src/po/po.cs.js
+++ b/web/src/po/po.cs.js
@@ -1385,9 +1385,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Čekám"
- ],
"Waiting for actions information...": [
"Čekáme na informace o akcích..."
],
diff --git a/web/src/po/po.de.js b/web/src/po/po.de.js
index 48f3518623..6704eecde4 100644
--- a/web/src/po/po.de.js
+++ b/web/src/po/po.de.js
@@ -1297,9 +1297,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Warten"
- ],
"Waiting for actions information...": [
"Warten auf Informationen zu Aktionen ..."
],
diff --git a/web/src/po/po.es.js b/web/src/po/po.es.js
index 25542512e7..ffacb84cbd 100644
--- a/web/src/po/po.es.js
+++ b/web/src/po/po.es.js
@@ -1390,9 +1390,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Esperar"
- ],
"Waiting for actions information...": [
"Esperando información de acciones..."
],
diff --git a/web/src/po/po.fr.js b/web/src/po/po.fr.js
index 8ba05207d9..87dd7c998f 100644
--- a/web/src/po/po.fr.js
+++ b/web/src/po/po.fr.js
@@ -1188,9 +1188,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "En attente"
- ],
"Wi-Fi": [
"Wi-Fi"
],
diff --git a/web/src/po/po.id.js b/web/src/po/po.id.js
index b2f56f6c0c..ef29a457a0 100644
--- a/web/src/po/po.id.js
+++ b/web/src/po/po.id.js
@@ -9,11 +9,32 @@ export default {
" and ": [
" dan "
],
+ "%1$s %2$s at %3$s (%4$s)": [
+ "%1$s %2$s pada %3$s (%4$s)"
+ ],
+ "%1$s %2$s partition (%3$s)": [
+ "Partisi %2$s %1$s (%3$s)"
+ ],
+ "%1$s %2$s volume (%3$s)": [
+ "Volume %2$s %1$s (%3$s)"
+ ],
+ "%1$s root at %2$s (%3$s)": [
+ "Root %1$s pada %2$s (%3$s)"
+ ],
+ "%1$s root partition (%2$s)": [
+ "Partisi root %1$s (%2$s)"
+ ],
+ "%1$s root volume (%2$s)": [
+ "Volume root %1$s (%2$s)"
+ ],
+ "%d partition will be shrunk": [
+ "Partisi %d akan menyusut"
+ ],
"%s disk": [
"%s diska"
],
"%s is an immutable system with atomic updates. It uses a read-only Btrfs file system updated via snapshots.": [
- "%s adalah sistem yang tidak dapat diubah dengan pembaruan atom. Ia menggunakan sistem file Btrfs yang hanya dapat dibaca yang diperbarui melalui snapshot."
+ "%s adalah sistem yang tidak dapat diubah (immutable) dan mendukung pembaruan atomik. Sistem ini menggunakan file system Btrfs yang hanya-baca dan diperbarui melalui snapshot."
],
"%s logo": [
"logo %s"
@@ -24,6 +45,9 @@ export default {
", ": [
", "
],
+ "A mount point is required": [
+ "Diperlukan mount point"
+ ],
"A new LVM Volume Group": [
"Grup Volume LVM baru"
],
@@ -36,12 +60,18 @@ export default {
"Accept": [
"Terima"
],
+ "Action": [
+ "Tindakan"
+ ],
"Actions": [
"Tindakan"
],
"Actions for connection %s": [
"Tindakan untuk koneksi %s"
],
+ "Actions to find space": [
+ "Tindakan untuk mencari ruang"
+ ],
"Activate": [
"Mengaktifkan"
],
@@ -57,6 +87,9 @@ export default {
"Activated": [
"Diaktifkan"
],
+ "Add %s file system": [
+ "Tambahkan sistem file %s"
+ ],
"Add DNS": [
"Tambahkan DNS"
],
@@ -84,9 +117,15 @@ export default {
"Addresses data list": [
"Daftar data alamat"
],
+ "All fields are required": [
+ "Semua bidang wajib diisi"
+ ],
"All partitions will be removed and any data in the disks will be lost.": [
"Semua partisi akan dihapus dan semua data dalam disk akan hilang."
],
+ "Allows to boot to a previous version of the system after configuration changes or software upgrades.": [
+ "Memungkinkan untuk melakukan booting ke versi sistem sebelumnya setelah perubahan konfigurasi atau peningkatan perangkat lunak."
+ ],
"Already set": [
"Sudah ditetapkan"
],
@@ -126,6 +165,12 @@ export default {
"Automatic (DHCP)": [
"Otomatis (DHCP)"
],
+ "Automatic LUN scan is [disabled]. LUNs have to be manually configured after activating a controller.": [
+ "Pemindaian LUN otomatis [dinonaktifkan]. LUN harus dikonfigurasi secara manual setelah mengaktifkan pengontrol."
+ ],
+ "Automatic LUN scan is [enabled]. Activating a controller which is running in NPIV mode will automatically configures all its LUNs.": [
+ "Pemindaian LUN otomatis [diaktifkan]. Mengaktifkan pengontrol yang berjalan dalam mode NPIV akan secara otomatis mengonfigurasi semua LUN."
+ ],
"Automatically calculated size according to the selected product.": [
"Ukuran yang dihitung secara otomatis menurut produk yang dipilih."
],
@@ -135,6 +180,9 @@ export default {
"Back": [
"Kembali"
],
+ "Back to device selection": [
+ "Kembali ke pemilihan perangkat"
+ ],
"Before %s": [
"Sebelum %s"
],
@@ -144,21 +192,45 @@ export default {
"Before starting the installation, you need to address the following problems:": [
"Sebelum memulai penginstalan, Anda perlu mengatasi masalah berikut ini:"
],
+ "Boot partitions at %s": [
+ "Partisi boot pada %s"
+ ],
+ "Boot partitions at installation disk": [
+ "Partisi boot pada disk instalasi"
+ ],
+ "Btrfs root partition with snapshots (%s)": [
+ "Partisi root btrfs dengan snapshot (%s)"
+ ],
+ "Btrfs root volume with snapshots (%s)": [
+ "Volume root Btrfs dengan snapshot (%s)"
+ ],
+ "Btrfs with snapshots": [
+ "Btrfs dengan snapshot"
+ ],
"Cancel": [
"Batal"
],
+ "Cannot accommodate the required file systems for installation": [
+ "Tidak dapat mengakomodasi sistem file yang diperlukan untuk instalasi"
+ ],
"Cannot be changed in remote installation": [
"Tidak dapat diubah dalam instalasi jarak jauh"
],
"Cannot connect to Agama server": [
"Tidak dapat terhubung ke server Agama"
],
+ "Cannot format all selected devices": [
+ "Tidak dapat memformat semua perangkat yang dipilih"
+ ],
"Change": [
"Ubah"
],
"Change boot options": [
"Mengubah opsi boot"
],
+ "Change location": [
+ "Ubah lokasi"
+ ],
"Change product": [
"Mengubah produk"
],
@@ -171,6 +243,9 @@ export default {
"Channel ID": [
"ID saluran"
],
+ "Check the planned action": [
+ "Periksa %d tindakan yang direncanakan"
+ ],
"Choose a disk for placing the boot loader": [
"Pilih disk untuk menempatkan boot loader"
],
@@ -231,9 +306,30 @@ export default {
"Could not log in. Please, make sure that the password is correct.": [
"Tidak dapat masuk. Harap pastikan kata sandi sudah benar."
],
+ "Create a dedicated LVM volume group": [
+ "Membuat grup volume LVM khusus"
+ ],
+ "Create a new partition": [
+ "Membuat partisi baru"
+ ],
+ "Create user": [
+ "Membuat pengguna"
+ ],
+ "Custom": [
+ "Khusus"
+ ],
+ "DASD": [
+ "DASD"
+ ],
"DASD %s": [
"DASD %s"
],
+ "DASD devices selection table": [
+ "Tabel pemilihan perangkat DASD"
+ ],
+ "DASDs table section": [
+ "Bagian tabel DASD"
+ ],
"DIAG": [
"DIAG"
],
@@ -255,6 +351,12 @@ export default {
"Delete current content": [
"Hapus konten saat ini"
],
+ "Destructive actions are allowed": [
+ "Tindakan destruktif diizinkan"
+ ],
+ "Destructive actions are not allowed": [
+ "Tindakan destruktif tidak diizinkan"
+ ],
"Details": [
"Detail"
],
@@ -297,6 +399,15 @@ export default {
"Do not configure": [
"Jangan konfigurasikan"
],
+ "Do not configure partitions for booting": [
+ "Jangan konfigurasikan partisi untuk booting"
+ ],
+ "Do you want to add it?": [
+ "Apakah Anda ingin menambahkannya?"
+ ],
+ "Do you want to edit it?": [
+ "Apakah Anda ingin mengeditnya?"
+ ],
"Download logs": [
"Unduh log"
],
@@ -306,11 +417,14 @@ export default {
"Edit %s": [
"Edit %s"
],
+ "Edit %s file system": [
+ "Mengedit sistem file %s"
+ ],
"Edit connection %s": [
"Edit koneksi %s"
],
"Edit file system": [
- "Edit sistem berkas"
+ "Edit sistem file"
],
"Edit iSCSI Initiator": [
"Mengedit Inisiator iSCSI"
@@ -321,9 +435,15 @@ export default {
"Edit the SSH Public Key for root": [
"Edit Kunci Publik SSH untuk root"
],
+ "Edit user": [
+ "Edit pengguna"
+ ],
"Enable": [
"Diaktifkan"
],
+ "Encrypt the system": [
+ "Mengenkripsi sistem"
+ ],
"Encrypted Device": [
"Perangkat Terenkripsi"
],
@@ -340,7 +460,16 @@ export default {
"Ukuran yang tepat untuk sistem berkas."
],
"File system type": [
- "Jenis sistem berkas"
+ "Jenis sistem file"
+ ],
+ "File systems created as new partitions at %s": [
+ "Sistem file yang dibuat sebagai partisi baru di %s"
+ ],
+ "File systems created at a new LVM volume group": [
+ "Sistem file yang dibuat di grup volume LVM baru"
+ ],
+ "File systems created at a new LVM volume group on %s": [
+ "Sistem file yang dibuat di grup volume LVM baru pada %s"
],
"Filter by description or keymap code": [
"Filter berdasarkan deskripsi atau kode peta kunci"
@@ -369,6 +498,9 @@ export default {
"Finished": [
"Selesai"
],
+ "First user": [
+ "Pengguna pertama"
+ ],
"Fixed": [
"Tetap"
],
@@ -381,6 +513,12 @@ export default {
"Format": [
"Format"
],
+ "Format selected devices?": [
+ "Memformat perangkat yang dipilih?"
+ ],
+ "Format the device": [
+ "Memformat perangkat"
+ ],
"Formatted": [
"Diformat"
],
@@ -549,12 +687,21 @@ export default {
"Localization": [
"Pelokalan"
],
+ "Location": [
+ "Lokasi"
+ ],
+ "Location for %s file system": [
+ "Lokasi untuk sistem file %s"
+ ],
"Log in": [
"Masuk"
],
"Log in as %s": [
"Masuk sebagai %s"
],
+ "Logical volume at system LVM": [
+ "Volume logis pada LVM sistem"
+ ],
"Login": [
"Masuk"
],
@@ -567,6 +714,9 @@ export default {
"Logout": [
"Keluar"
],
+ "Main disk or LVM Volume Group for installation.": [
+ "Disk utama atau Grup Volume LVM untuk instalasi."
+ ],
"Main navigation": [
"Navigasi utama"
],
@@ -613,7 +763,10 @@ export default {
"Ubah"
],
"More info for file system types": [
- "Info lebih lanjut untuk jenis sistem berkas"
+ "Info lebih lanjut untuk jenis sistem file"
+ ],
+ "Mount %1$s at %2$s (%3$s)": [
+ "Mount %1$s di %2$s (%3$s)"
],
"Mount Point": [
"Mount Point"
@@ -621,6 +774,9 @@ export default {
"Mount point": [
"Titik pemasangan"
],
+ "Mount the file system": [
+ "Mount sistem file"
+ ],
"Multipath": [
"Multipath"
],
@@ -723,6 +879,15 @@ export default {
"Partition Info": [
"Info Partisi"
],
+ "Partition at %s": [
+ "Partisi pada %s"
+ ],
+ "Partition at installation disk": [
+ "Partisi pada disk instalasi"
+ ],
+ "Partitions and file systems": [
+ "Partisi dan sistem file"
+ ],
"Partitions to boot will be allocated at the following device.": [
"Partisi untuk boot akan dialokasikan pada perangkat berikut."
],
@@ -741,6 +906,9 @@ export default {
"Password confirmation": [
"Konfirmasi kata sandi"
],
+ "Password for root user": [
+ "Kata sandi untuk pengguna root"
+ ],
"Password input": [
"Masukan kata sandi"
],
@@ -837,6 +1005,9 @@ export default {
"Remove min channel filter": [
"Menghapus filter saluran min"
],
+ "Reset location": [
+ "Atur ulang lokasi"
+ ],
"Reset to defaults": [
"Mengatur ulang ke default"
],
@@ -876,15 +1047,27 @@ export default {
"Select a disk": [
"Pilih disk"
],
+ "Select a location": [
+ "Pilih lokasi"
+ ],
"Select a product": [
"Pilih produk"
],
"Select booting partition": [
"Pilih partisi booting"
],
+ "Select how to allocate the file system": [
+ "Pilih cara mengalokasikan sistem file"
+ ],
+ "Select in which device to allocate the file system": [
+ "Pilih perangkat mana yang akan dialokasikan sebagai sistem file"
+ ],
"Select installation device": [
"Pilih perangkat instalasi"
],
+ "Select what to do with each partition.": [
+ "Pilih apa yang akan dilakukan dengan setiap partisi."
+ ],
"Selected patterns": [
"Pola yang dipilih"
],
@@ -912,12 +1095,30 @@ export default {
"Set root SSH public key": [
"Atur kunci publik SSH root"
],
+ "Setup root user authentication": [
+ "Menyiapkan autentikasi pengguna root"
+ ],
"Show %d subvolume action": [
"Tampilkan tindakan subvolume %d"
],
+ "Show information about %s": [
+ "Menampilkan informasi tentang %s"
+ ],
+ "Show partitions and file-systems actions": [
+ "Menampilkan tindakan partisi dan sistem file"
+ ],
"Shrink existing partitions": [
"Perkecil partisi yang ada"
],
+ "Shrinking partitions is allowed": [
+ "Menyusutkan partisi diizinkan"
+ ],
+ "Shrinking partitions is not allowed": [
+ "Menyusutkan partisi tidak diizinkan"
+ ],
+ "Shrinking some partitions is allowed but not needed": [
+ "Menyusutkan beberapa partisi diizinkan tetapi tidak diperlukan"
+ ],
"Size": [
"Ukuran"
],
@@ -936,6 +1137,9 @@ export default {
"Something went wrong": [
"Ada yang tidak beres"
],
+ "Space policy": [
+ "Kebijakan ruang"
+ ],
"Startup": [
"Startup"
],
@@ -951,6 +1155,15 @@ export default {
"Structure of the new system, including any additional partition needed for booting": [
"Struktur sistem baru, termasuk partisi tambahan yang diperlukan untuk booting"
],
+ "Swap at %1$s (%2$s)": [
+ "Swap di %1$s (%2$s)"
+ ],
+ "Swap partition (%s)": [
+ "Partisi swap (%s)"
+ ],
+ "Swap volume (%s)": [
+ "Volume swap (%s)"
+ ],
"TPM sealing requires the new system to be booted directly.": [
"Penyegelan TPM mengharuskan sistem baru untuk di-boot secara langsung."
],
@@ -975,15 +1188,30 @@ export default {
"The content may be deleted": [
"Konten mungkin akan dihapus"
],
+ "The current file system on %s is selected to be mounted at %s.": [
+ "Sistem file saat ini pada %s dipilih untuk dimount pada %s."
+ ],
"The current file system on the selected device will be mounted without formatting the device.": [
"Sistem file saat ini pada perangkat yang dipilih akan dipasang tanpa memformat perangkat."
],
+ "The data is kept, but the current partitions will be resized as needed.": [
+ "Data tetap dipertahankan, tetapi partisi saat ini akan diubah ukurannya sesuai kebutuhan."
+ ],
+ "The data is kept. Only the space not assigned to any partition will be used.": [
+ "Data tetap dipertahankan. Hanya ruang yang tidak ditetapkan ke partisi mana pun yang akan digunakan."
+ ],
"The device cannot be shrunk:": [
"Perangkat tidak dapat dikecilkan:"
],
"The encryption password did not work": [
"Kata sandi enkripsi tidak berfungsi"
],
+ "The file system is allocated at the device %s.": [
+ "Sistem file dialokasikan pada perangkat %s."
+ ],
+ "The file system will be allocated as a new partition at the selected disk.": [
+ "Sistem file akan dialokasikan sebagai partisi baru pada disk yang dipilih."
+ ],
"The file systems are allocated at the installation device by default. Indicate a custom location to create the file system at a specific device.": [
"Sistem file dialokasikan pada perangkat instalasi secara default. Tunjukkan lokasi khusus untuk membuat sistem file di perangkat tertentu."
],
@@ -1014,12 +1242,21 @@ export default {
"The installer requires [root] user privileges.": [
"Penginstal memerlukan hak akses pengguna [root]."
],
+ "The mount point is invalid": [
+ "Mount point tidak valid"
+ ],
"The options for the file system type depends on the product and the mount point.": [
- "Opsi untuk jenis sistem berkas tergantung pada produk dan titik mount."
+ "Opsi untuk jenis sistem file tergantung pada produk dan mount point."
],
"The password will not be needed to boot and access the data if the TPM can verify the integrity of the system. TPM sealing requires the new system to be booted directly on its first run.": [
"Kata sandi tidak akan diperlukan untuk mem-boot dan mengakses data jika TPM dapat memverifikasi integritas sistem. Penyegelan TPM mengharuskan sistem baru untuk di-boot secara langsung saat pertama kali dijalankan."
],
+ "The selected device will be formatted as %s file system.": [
+ "Perangkat yang dipilih akan diformat sebagai sistem file %s."
+ ],
+ "The size of the file system cannot be edited": [
+ "Ukuran sistem file tidak dapat diedit"
+ ],
"The system does not support Wi-Fi connections, probably because of missing or disabled hardware.": [
"Sistem tidak mendukung koneksi Wi-Fi, mungkin karena perangkat keras yang tidak ada atau dinonaktifkan."
],
@@ -1032,9 +1269,18 @@ export default {
"The systems will be configured as displayed below.": [
"Sistem akan dikonfigurasikan seperti yang ditampilkan di bawah ini."
],
+ "The type and size of the file system cannot be edited.": [
+ "Jenis dan ukuran sistem file tidak dapat diedit."
+ ],
"The zFCP disk was not activated.": [
"Disk zFCP tidak diaktifkan."
],
+ "There is a predefined file system for %s.": [
+ "Ada sistem file yang telah ditentukan untuk %s."
+ ],
+ "There is already a file system for %s.": [
+ "Sudah ada sistem file untuk %s."
+ ],
"These are the most relevant installation settings. Feel free to browse the sections in the menu for further details.": [
"Berikut ini adalah pengaturan instalasi yang paling relevan. Silakan telusuri bagian dalam menu untuk rincian lebih lanjut."
],
@@ -1059,6 +1305,15 @@ export default {
"To ensure the new system is able to boot, the installer may need to create or configure some partitions in the appropriate disk.": [
"Untuk memastikan sistem baru dapat melakukan booting, penginstal mungkin perlu membuat atau mengonfigurasi beberapa partisi di disk yang sesuai."
],
+ "Transactional Btrfs": [
+ "Btrfs Transaksional"
+ ],
+ "Transactional Btrfs root partition (%s)": [
+ "Partisi root Btrfs transaksional (%s)"
+ ],
+ "Transactional Btrfs root volume (%s)": [
+ "Volume root Btrfs transaksional (%s)"
+ ],
"Transactional root file system": [
"Sistem file root transaksional"
],
@@ -1092,6 +1347,9 @@ export default {
"Usage": [
"Penggunaan"
],
+ "Use Btrfs snapshots for the root file system": [
+ "Gunakan snapshot Btrfs untuk sistem file root"
+ ],
"Use available space": [
"Gunakan ruang yang tersedia"
],
@@ -1128,8 +1386,11 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Menunggu"
+ "Waiting for actions information...": [
+ "Menunggu informasi tindakan..."
+ ],
+ "Waiting for information about storage configuration": [
+ "Menunggu informasi tentang konfigurasi penyimpanan"
],
"Wi-Fi": [
"Wi-Fi"
@@ -1146,9 +1407,18 @@ export default {
"Yes": [
"Ya"
],
+ "You can change it or select another authentication method in the 'Users' section before installing.": [
+ "Anda dapat mengubahnya atau memilih metode autentikasi lain di bagian 'Pengguna' sebelum menginstal."
+ ],
"ZFCP": [
"ZFCP"
],
+ "affecting": [
+ "mempengaruhi"
+ ],
+ "at least %s": [
+ "setidaknya %s"
+ ],
"auto": [
"otomatis"
],
@@ -1158,6 +1428,9 @@ export default {
"configured": [
"dikonfigurasi"
],
+ "deleting current content": [
+ "menghapus konten saat ini"
+ ],
"disabled": [
"dinonaktifkan"
],
@@ -1170,6 +1443,9 @@ export default {
"iSCSI": [
"iSCSI"
],
+ "shrinking partitions": [
+ "menyusutkan partisi"
+ ],
"storage techs": [
"teknisi penyimpanan"
],
@@ -1188,6 +1464,9 @@ export default {
"using TPM unlocking": [
"menggunakan pembukaan kunci TPM"
],
+ "with custom actions": [
+ "dengan tindakan khusus"
+ ],
"without modifying any partition": [
"tanpa memodifikasi partisi apa pun"
],
diff --git a/web/src/po/po.ja.js b/web/src/po/po.ja.js
index 366b047ee4..d13e788209 100644
--- a/web/src/po/po.ja.js
+++ b/web/src/po/po.ja.js
@@ -1386,9 +1386,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "お待ちください"
- ],
"Waiting for actions information...": [
"処理に関する情報を待機しています..."
],
diff --git a/web/src/po/po.nb_NO.js b/web/src/po/po.nb_NO.js
index 2c10ab4eb2..a1653096f6 100644
--- a/web/src/po/po.nb_NO.js
+++ b/web/src/po/po.nb_NO.js
@@ -1318,9 +1318,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Venter"
- ],
"Waiting for actions information...": [
"Venter på handlingsinformasjon..."
],
diff --git a/web/src/po/po.pt_BR.js b/web/src/po/po.pt_BR.js
index cfc2179cf9..e632e556cb 100644
--- a/web/src/po/po.pt_BR.js
+++ b/web/src/po/po.pt_BR.js
@@ -1390,9 +1390,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Aguardando"
- ],
"Waiting for actions information...": [
"Aguardando informações sobre ações..."
],
diff --git a/web/src/po/po.ru.js b/web/src/po/po.ru.js
index 2849d490b2..a81197417e 100644
--- a/web/src/po/po.ru.js
+++ b/web/src/po/po.ru.js
@@ -1328,9 +1328,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Ожидание"
- ],
"Waiting for actions information...": [
"Ожидание информации о действиях..."
],
diff --git a/web/src/po/po.sv.js b/web/src/po/po.sv.js
index d84d6c5630..fe240d27cc 100644
--- a/web/src/po/po.sv.js
+++ b/web/src/po/po.sv.js
@@ -1390,9 +1390,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Väntande"
- ],
"Waiting for actions information...": [
"Väntar på åtgärdsinformation..."
],
diff --git a/web/src/po/po.tr.js b/web/src/po/po.tr.js
index 5123c5456d..9e960d3826 100644
--- a/web/src/po/po.tr.js
+++ b/web/src/po/po.tr.js
@@ -1378,9 +1378,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "Bekleyin"
- ],
"Waiting for actions information...": [
"Eylem bilgileri bekleniyor..."
],
diff --git a/web/src/po/po.zh_Hans.js b/web/src/po/po.zh_Hans.js
index 721536656f..272599ee17 100644
--- a/web/src/po/po.zh_Hans.js
+++ b/web/src/po/po.zh_Hans.js
@@ -1308,9 +1308,6 @@ export default {
"WWPN": [
"WWPN"
],
- "Waiting": [
- "正在等候"
- ],
"Waiting for actions information...": [
"正在等待操作信息……"
],
From d994b62595a0007930a4288c0b20179ebc438ce3 Mon Sep 17 00:00:00 2001
From: YaST Bot
Date: Sun, 5 Jan 2025 03:07:33 +0000
Subject: [PATCH 074/147] Update service PO files
Agama-weblate commit: 49c2ce54826ea6d04ce8fb806a4bd9fbe1a5a8a2
---
service/po/nl.po | 316 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 316 insertions(+)
create mode 100644 service/po/nl.po
diff --git a/service/po/nl.po b/service/po/nl.po
new file mode 100644
index 0000000000..f54b68cd71
--- /dev/null
+++ b/service/po/nl.po
@@ -0,0 +1,316 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR SuSE Linux Products GmbH, Nuernberg
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-01-02 02:38+0000\n"
+"PO-Revision-Date: 2025-01-02 16:50+0000\n"
+"Last-Translator: Natasha Ament \n"
+"Language-Team: Dutch \n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 5.9.2\n"
+
+#. Runs the config phase
+#: service/lib/agama/manager.rb:93
+msgid "Analyze disks"
+msgstr "Analyseer schijven"
+
+#: service/lib/agama/manager.rb:93
+msgid "Configure software"
+msgstr "Configureer software"
+
+#. Runs the install phase
+#. rubocop:disable Metrics/AbcSize
+#: service/lib/agama/manager.rb:116
+msgid "Prepare disks"
+msgstr "Prepareer schijven"
+
+#: service/lib/agama/manager.rb:117
+msgid "Install software"
+msgstr "Installeer software"
+
+#: service/lib/agama/manager.rb:118
+msgid "Configure the system"
+msgstr "Configureer het systeem"
+
+#. rubocop:enable Metrics/AbcSize
+#: service/lib/agama/manager.rb:156
+msgid "Load software translations"
+msgstr "Laad software vertalingen"
+
+#: service/lib/agama/manager.rb:157
+msgid "Load storage translations"
+msgstr "Laad opslag vertalingen"
+
+#. Callback to handle unsigned files
+#.
+#. @param filename [String] File name
+#. @param repo_id [Integer] Repository ID. It might be -1 if there is not an associated repo.
+#: service/lib/agama/software/callbacks/signature.rb:63
+#, perl-brace-format
+msgid "The file %{filename} from repository %{repo_name} (%{repo_url})"
+msgstr "Het bestand %{filename} van opslagruimte %{repo_name} (%{repo_url})"
+
+#: service/lib/agama/software/callbacks/signature.rb:67
+#, perl-brace-format
+msgid "The file %{filename}"
+msgstr "Het bestand %{filename}"
+
+#: service/lib/agama/software/callbacks/signature.rb:71
+#, perl-brace-format
+msgid ""
+"%{source} is not digitally signed. The origin and integrity of the file cannot"
+" be verified. Use it anyway?"
+msgstr ""
+"%{source} is niet digitaal ondertekend. De herkomst en integriteit van het "
+"bestand kan niet worden gecontroleerd. Toch gebruiken?"
+
+#. Callback to handle signature verification failures
+#.
+#. @param key [Hash] GPG key data (id, name, fingerprint, etc.)
+#. @param _repo_id [Integer] Repository ID
+#: service/lib/agama/software/callbacks/signature.rb:94
+#, perl-brace-format
+msgid ""
+"The key %{id} (%{name}) with fingerprint %{fingerprint} is unknown. Do you wan"
+"t to trust this key?"
+msgstr ""
+"De sleutel {id} (%{name}) met vingerafdruk %{fingerprint} is onbekend. "
+"Vertrouwt u deze sleutel?"
+
+#. Should an error be raised?
+#: service/lib/agama/software/manager.rb:141
+msgid "Initializing sources"
+msgstr "Bronnen initialiseren"
+
+#: service/lib/agama/software/manager.rb:146
+msgid "Refreshing repositories metadata"
+msgstr "Metadata van opslagruimtes verversen"
+
+#: service/lib/agama/software/manager.rb:147
+msgid "Calculating the software proposal"
+msgstr "Berekenen van het softwarevoorstel"
+
+#. Issues related to the software proposal.
+#.
+#. Repositories that could not be probed are reported as errors.
+#.
+#. @return [Array]
+#: service/lib/agama/software/manager.rb:563
+#, c-format
+msgid "Could not read repository \"%s\""
+msgstr "Kan opslagruimte \"%s\" niet lezen"
+
+#. Issue when a product is missing
+#.
+#. @return [Agama::Issue]
+#: service/lib/agama/software/manager.rb:573
+msgid "Product not selected yet"
+msgstr "Nog geen product geselecteerd"
+
+#. Issue when a product requires registration but it is not registered yet.
+#.
+#. @return [Agama::Issue]
+#: service/lib/agama/software/manager.rb:582
+msgid "Product must be registered"
+msgstr "Product moet geregistreerd zijn"
+
+#. Returns solver error messages from the last attempt
+#.
+#. @return [Array] Error messages
+#: service/lib/agama/software/proposal.rb:223
+#, c-format
+msgid "Found %s dependency issues."
+msgstr "%s afhankelijkheidsissues gevonden."
+
+#. Issue for not found device.
+#.
+#. @param config [Configs::Drive, Configs::Partition]
+#. @return [Agama::Issue]
+#: service/lib/agama/storage/config_checker.rb:87
+msgid "No device found for a mandatory drive"
+msgstr "Geen apparaat gevonden voor een vereiste schijf"
+
+#: service/lib/agama/storage/config_checker.rb:89
+msgid "No device found for a mandatory partition"
+msgstr "Geen apparaat gevonden voor een vereiste partitie"
+
+#. TRANSLATORS: %s is the replaced by a mount path (e.g., "/home").
+#: service/lib/agama/storage/config_checker.rb:118
+#, c-format
+msgid "Missing file system type for '%s'"
+msgstr "Ontbrekend bestandssysteem voor '%s'"
+
+#. TRANSLATORS: %{filesystem} is replaced by a file system type (e.g., "Btrfs") and
+#. %{path} is replaced by a mount path (e.g., "/home").
+#: service/lib/agama/storage/config_checker.rb:145
+#, perl-brace-format
+msgid "The file system type '%{filesystem}' is not suitable for '%{path}'"
+msgstr "Het bestandssysteemtype '%{filesystem}' is niet geschikt voor '%{path}'"
+
+#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
+#. (e.g., 'luks1', 'random_swap').
+#: service/lib/agama/storage/config_checker.rb:178
+#, perl-brace-format
+msgid "No passphrase provided (required for using the method '%{crypt_method}')."
+msgstr ""
+"Geen wachtwoordzin opgegeven (vereist bij gebruik van methode "
+"'%{crypt_method}')."
+
+#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
+#. (e.g., 'luks1', 'random_swap').
+#: service/lib/agama/storage/config_checker.rb:196
+#, perl-brace-format
+msgid "Encryption method '%{crypt_method}' is not available in this system."
+msgstr "Encryptie methode '%{crypt_method}' is niet beschikbaar op dit systeem."
+
+#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
+#. (e.g., 'luks1', 'random_swap').
+#: service/lib/agama/storage/config_checker.rb:226
+#, perl-brace-format
+msgid "'%{crypt_method}' is not a suitable method to encrypt the device."
+msgstr ""
+"'%{crypt_method}' is een niet geschikte methode om het apparaat te "
+"versleutelen."
+
+#. TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
+#: service/lib/agama/storage/config_checker.rb:276
+#, c-format
+msgid "The device '%s' is used several times as target device for physical volumes"
+msgstr ""
+"Het apparaat '%s' is meerdere keren gebruikt al doel apparaat voor fysieke "
+"volumes"
+
+#. TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
+#: service/lib/agama/storage/config_checker.rb:350
+#, c-format
+msgid "There is no LVM thin pool volume with alias '%s'"
+msgstr "Er is geen LVM thin pool volume met alias '%s'"
+
+#. TRANSLATORS: %s is the replaced by a device alias (e.g., "pv1").
+#: service/lib/agama/storage/config_checker.rb:375
+#, c-format
+msgid "There is no LVM physical volume with alias '%s'"
+msgstr "Er is geen fysiek LVM volume met alias '%s'"
+
+#. TRANSLATORS: %s is the replaced by a device alias (e.g., "disk1").
+#: service/lib/agama/storage/config_checker.rb:401
+#, c-format
+msgid "There is no target device for LVM physical volumes with alias '%s'"
+msgstr "Er is geen doelapparaat voor fysieke LVM volumes met alias '%s'"
+
+#. TRANSLATORS: 'crypt_method' is the identifier of the method to encrypt the device
+#. (e.g., 'luks1').
+#: service/lib/agama/storage/config_checker.rb:434
+#, perl-brace-format
+msgid "'%{crypt_method}' is not a suitable method to encrypt the physical volumes."
+msgstr ""
+"'%{crypt_method}' is geen geschikte methode voor het versleutelen van "
+"fysieke volumes."
+
+#. Text of the reason preventing to shrink because there is no content.
+#.
+#. @return [String, nil] nil if there is content or there is any other reasons.
+#: service/lib/agama/storage/device_shrinking.rb:151
+msgid ""
+"Neither a file system nor a storage system was detected on the device. In case"
+" the device does contain a file system or a storage system that is not support"
+"ed, resizing will most likely cause data loss."
+msgstr ""
+"Er is geen bestandssysteem of opslag systeem gedetecteerd op het apparaat. "
+"In het geval dat het apparaat wel een bestandssysteem of opslagsysteem dat "
+"niet wordt ondersteund, zal het aanpassen van de grootte zeer waarschijnlijk "
+"leiden tot gegevensverlies."
+
+#. Text of the reason preventing to shrink because there is no valid minimum size.
+#.
+#. @return [String, nil] nil if there is a minimum size or there is any other reasons.
+#: service/lib/agama/storage/device_shrinking.rb:162
+msgid "Shrinking is not supported by this device"
+msgstr "Verkleinen wordt niet ondersteund door dit apparaat"
+
+#. Probes storage devices and performs an initial proposal
+#: service/lib/agama/storage/manager.rb:120
+msgid "Activating storage devices"
+msgstr "Activeren van opslag apparaten"
+
+#: service/lib/agama/storage/manager.rb:121
+msgid "Probing storage devices"
+msgstr "Onderzoeken van opslag apparaten"
+
+#: service/lib/agama/storage/manager.rb:122
+msgid "Calculating the storage proposal"
+msgstr "Berekenen van het opslagvoorstel"
+
+#: service/lib/agama/storage/manager.rb:123
+msgid "Selecting Linux Security Modules"
+msgstr "Selecteren van Linux beveiligingsmodules"
+
+#. Prepares the partitioning to install the system
+#: service/lib/agama/storage/manager.rb:131
+msgid "Preparing bootloader proposal"
+msgstr "Voorbereiden bootloader voorstel"
+
+#. then also apply changes to that proposal
+#: service/lib/agama/storage/manager.rb:138
+msgid "Adding storage-related packages"
+msgstr "Toevoegen opslag gerelateerde pakketten"
+
+#: service/lib/agama/storage/manager.rb:139
+msgid "Preparing the storage devices"
+msgstr "Voorbereiden van de opslag apparaten"
+
+#: service/lib/agama/storage/manager.rb:140
+msgid "Writing bootloader sysconfig"
+msgstr "Schrijven van de bootloader sysconfig"
+
+#. Issue representing the proposal is not valid.
+#.
+#. @return [Issue]
+#: service/lib/agama/storage/proposal.rb:287
+msgid "Cannot accommodate the required file systems for installation"
+msgstr "Kan de vereiste bestandssystemen voor installatie niet toepassen"
+
+#. Issue to communicate a generic Y2Storage error.
+#.
+#. @return [Issue]
+#: service/lib/agama/storage/proposal.rb:298
+msgid "A problem ocurred while calculating the storage setup"
+msgstr "Een probleem is ontstaan bij het berekenen van de opslag opstelling"
+
+#. Returns an issue if there is no target device.
+#.
+#. @return [Issue, nil]
+#: service/lib/agama/storage/proposal_strategies/guided.rb:127
+msgid "No device selected for installation"
+msgstr "Geen apparaat geselecteerd voor installatie"
+
+#. Returns an issue if any of the devices required for the proposal is not found
+#.
+#. @return [Issue, nil]
+#: service/lib/agama/storage/proposal_strategies/guided.rb:143
+#, perl-brace-format
+msgid "The following selected device is not found in the system: %{devices}"
+msgid_plural "The following selected devices are not found in the system: %{devices}"
+msgstr[0] ""
+"Het volgende, geselecteerde apparaat is niet gevonden in het systeem: "
+"%{devices}"
+msgstr[1] ""
+"De volgende, geselecteerde apparaten zij niet gevonden in het systeem: "
+"%{devices}"
+
+#. Recalculates the list of issues
+#: service/lib/agama/users.rb:154
+msgid "Defining a user, setting the root password or a SSH public key is required"
+msgstr ""
+"Het aanmaken van een gebruiker, het instellen van een root password of een "
+"publieke SSH sleutel is vereist"
From bbe522dcc65e4517f4670002e81798a41acfbc54 Mon Sep 17 00:00:00 2001
From: AngelaBriel
Date: Mon, 6 Jan 2025 15:49:37 +0100
Subject: [PATCH 075/147] change product description for SLES4SAP (bsc#1235023)
---
products.d/agama-products.changes | 7 +++++
products.d/sles_sap_160.yaml | 46 ++++---------------------------
2 files changed, 12 insertions(+), 41 deletions(-)
diff --git a/products.d/agama-products.changes b/products.d/agama-products.changes
index 0470f30118..3c0380ef27 100644
--- a/products.d/agama-products.changes
+++ b/products.d/agama-products.changes
@@ -1,3 +1,10 @@
+-------------------------------------------------------------------
+Mon Jan 6 14:41:28 UTC 2025 - Angela Briel
+
+- SLES for SAP Application product:
+ Change product description.
+ (bsc#1235023)
+
-------------------------------------------------------------------
Thu Dec 5 11:04:06 UTC 2024 - Angela Briel
diff --git a/products.d/sles_sap_160.yaml b/products.d/sles_sap_160.yaml
index 8b8db18ea8..d48e249357 100644
--- a/products.d/sles_sap_160.yaml
+++ b/products.d/sles_sap_160.yaml
@@ -1,54 +1,18 @@
id: SLES_SAP_16.0
-name: SUSE Linux Enterprise Server for SAP Applications 16.0 Alpha
+name: SUSE Linux Enterprise Server for SAP Applications 16.0 Beta
# ------------------------------------------------------------------------------
# WARNING: When changing the product description delete the translations located
# at the at translations/description key below to avoid using obsolete
# translations!!
# ------------------------------------------------------------------------------
-description: "An open, reliable, compliant, and future-proof Linux Server choice
- that ensures the enterprise's business continuity. It is the secure and
- adaptable OS for long-term supported, innovation-ready infrastructure running
- business-critical workloads on-premises, in the cloud, and at the edge."
+description: "The leading OS for a secure and reliable SAP platform.
+Endorsed for SAP deployments, SUSE Linux Enterprise Server for SAP Applications
+futureproofs the SAP project, offers uninterrupted business, and minimizes
+operational risks and costs."
icon: SUSE.svg
# Do not manually change any translations! See README.md for more details.
translations:
description:
- ca: Una opció de servidor de Linux oberta, fiable, compatible i a prova del
- futur que garanteix la continuïtat del negoci de l'empresa. És el sistema
- operatiu segur i adaptable per a una infraestructura amb suport a llarg
- termini i preparada per a la innovació que executa càrregues de treball
- crítiques per a l'empresa a les instal·lacions, al núvol i a l'última.
- cs: Otevřená, spolehlivá, kompatibilní a perspektivní volba linuxového serveru,
- která zajišťuje kontinuitu podnikání podniku. Je to bezpečný a
- přizpůsobivý operační systém pro dlouhodobě podporovanou infrastrukturu
- připravenou na inovace, na které běží kritické podnikové úlohy v lokálním
- prostředí, v cloudu i na okraji sítě.
- de: Ein offener, zuverlässiger, kompatibler und zukunftssicherer Linux-Server,
- der die Geschäftskontinuität des Unternehmens gewährleistet. Es ist das
- sichere und anpassungsfähige Betriebssystem für eine langfristig
- unterstützte, innovationsbereite Infrastruktur, auf der geschäftskritische
- Arbeitslasten vor Ort, in der Cloud und am Netzwerkrand ausgeführt werden.
- es: Una opción de servidor Linux abierta, confiable, compatible y preparada para
- el futuro que garantiza la continuidad del negocio de la empresa. Es el
- sistema operativo seguro y adaptable para una infraestructura lista para
- la innovación y con soporte a largo plazo que ejecuta cargas de trabajo
- críticas para el negocio en las instalaciones, en la nube y en el borde.
- ja: オープンで信頼性が高く、各種の標準にも準拠し、将来性とビジネスの継続性を支援する Linux
- サーバです。長期のサポートが提供されていることから、安全性と順応性に優れ、オンプレミスからクラウド、エッジ環境に至るまで、様々な場所で重要なビジネス処理をこなすことのできる革新性の高いインフラストラクチャです。
- pt_BR: Uma escolha de servidor Linux aberta, confiável, compatível e à prova do
- futuro que garante a continuidade dos negócios da empresa. É o SO seguro e
- adaptável para infraestrutura com suporte de longo prazo e pronta para
- inovação, executando cargas de trabalho críticas para os negócios no
- local, na nuvem e na borda.
- sv: Ett öppet, pålitligt, kompatibelt och framtidssäkert Linux-serverval som
- säkerställer företagets affärskontinuitet. Det är det säkra och
- anpassningsbara operativsystemet för långsiktigt stödd, innovationsfärdig
- infrastruktur som kör affärskritiska arbetsbelastningar på plats, i molnet
- och vid kanten.
- tr: İşletmenin iş sürekliliğini garanti eden açık, güvenilir, uyumlu ve geleceğe
- dönük bir Linux Sunucu seçeneği. Uzun vadeli desteklenen, inovasyona hazır
- altyapı için güvenli ve uyarlanabilir işletim sistemidir. Şirket içinde,
- bulutta ve uçta iş açısından kritik iş yüklerini çalıştırır.
software:
installation_repositories:
# Use plain HTTP repositories, HTTPS does not work without importing the SSL
From 5830f3f0ef3ffe1bb9f2207bc09e8bbc843e94d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?=
Date: Mon, 6 Jan 2025 17:24:26 +0100
Subject: [PATCH 076/147] Drop the integration tests code, update the CI
- The code has been moved to a separate GitHub repository
https://github.com/agama-project/integration-tests
- Update the CI accordingly
---
.github/workflows/ci-integration-tests.yml | 19 +-
.../obs-staging-integration-tests.yml | 24 -
puppeteer/.gitignore | 2 -
puppeteer/LICENSE | 339 --
puppeteer/README.md | 93 -
puppeteer/agama-integration-tests | 34 -
puppeteer/node-prune.sh | 159 -
puppeteer/node-puppeteer-prune.sh | 9 -
puppeteer/package-lock.json | 2794 -----------------
puppeteer/package.json | 8 -
puppeteer/package/_service | 30 -
.../package/agama-integration-tests.changes | 22 -
.../package/agama-integration-tests.spec | 77 -
puppeteer/tests/test_root_password.js | 197 --
14 files changed, 12 insertions(+), 3795 deletions(-)
delete mode 100644 .github/workflows/obs-staging-integration-tests.yml
delete mode 100644 puppeteer/.gitignore
delete mode 100644 puppeteer/LICENSE
delete mode 100644 puppeteer/README.md
delete mode 100755 puppeteer/agama-integration-tests
delete mode 100755 puppeteer/node-prune.sh
delete mode 100755 puppeteer/node-puppeteer-prune.sh
delete mode 100644 puppeteer/package-lock.json
delete mode 100644 puppeteer/package.json
delete mode 100644 puppeteer/package/_service
delete mode 100644 puppeteer/package/agama-integration-tests.changes
delete mode 100644 puppeteer/package/agama-integration-tests.spec
delete mode 100644 puppeteer/tests/test_root_password.js
diff --git a/.github/workflows/ci-integration-tests.yml b/.github/workflows/ci-integration-tests.yml
index 722c67c7e6..d96017f744 100644
--- a/.github/workflows/ci-integration-tests.yml
+++ b/.github/workflows/ci-integration-tests.yml
@@ -28,6 +28,17 @@ jobs:
fetch-depth: 0
fetch-tags: true
+ - name: Checkout integration tests
+ uses: actions/checkout@v4
+ with:
+ path: integration-tests
+ repository: ${{ github.repository_owner }}/integration-tests
+ fetch-depth: 1
+
+ - name: Build integration test
+ run: npm ci && ESLINT=0 npm run build
+ working-directory: ./integration-tests
+
- name: Created shared YaST log directory
run: mkdir -p /tmp/log/YaST2 /tmp/log/puppeteer
@@ -71,16 +82,10 @@ jobs:
- name: Run the Agama smoke test
run: podman exec agama curl http://localhost
- - name: Run the Puppeteer tests
- # update the test file and the runner script from git
- run: podman exec agama bash -c
- "cp /checkout/puppeteer/tests/test_root_password.js /usr/share/agama/integration-tests/tests &&
- cp /checkout/puppeteer/agama-integration-tests /usr/bin/agama-integration-tests"
-
- name: Run the Puppeteer tests
# run the test
run: podman exec agama bash -c "cd /var/log/puppeteer &&
- agama-integration-tests /usr/share/agama/integration-tests/tests/test_root_password.js"
+ node --enable-source-maps --test-reporter=spec /checkout/integration-tests/dist/test_root_password.js"
- name: Again show the D-Bus services log
# run even when any previous step fails
diff --git a/.github/workflows/obs-staging-integration-tests.yml b/.github/workflows/obs-staging-integration-tests.yml
deleted file mode 100644
index 63c26e2461..0000000000
--- a/.github/workflows/obs-staging-integration-tests.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: Submit agama-integration-tests
-
-on:
- # runs on pushes targeting the default branch
- push:
- branches:
- - master
- - release
- paths:
- # run only when a source file is changed
- - puppeteer/**
-
- # allow running manually
- workflow_dispatch:
-
-jobs:
- update_staging:
- uses: ./.github/workflows/obs-staging-shared.yml
- # pass all secrets
- secrets: inherit
- with:
- install_packages: obs-service-node_modules
- package_name: agama-integration-tests
- service_file: puppeteer/package/_service
diff --git a/puppeteer/.gitignore b/puppeteer/.gitignore
deleted file mode 100644
index f2b93381e9..0000000000
--- a/puppeteer/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/
-log/
diff --git a/puppeteer/LICENSE b/puppeteer/LICENSE
deleted file mode 100644
index d159169d10..0000000000
--- a/puppeteer/LICENSE
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- , 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/puppeteer/README.md b/puppeteer/README.md
deleted file mode 100644
index 6b0ce8b431..0000000000
--- a/puppeteer/README.md
+++ /dev/null
@@ -1,93 +0,0 @@
-# Agama Integration Tests
-
-This is directory contains support writing integration tests for Agama. It is
-based on the [Puppeteer](https://pptr.dev/) library.
-
-Currently there is only one simple test which only selects the product to
-install and sets the root password. More tests will be implemented separately in
-the openQA, this package basically just ensures that the needed libraries and
-tools are present on the Live ISO.
-
-## Running Tests
-
-The integration tests can be started from a Git checkout or from a Live ISO.
-
-### Live ISO
-
-To run the test from Live ISO:
-
-```sh
-agama-integration-tests /usr/share/agama/integration-tests/tests/test_root_password.js
-```
-
-This runs a headless test which expects the Agama is running on the local
-machine. See the [Options](#options) section below how to customize the test
-run.
-
-### Git
-
-To run the test directly from Git checkout:
-
-```sh
-./agama-integration-tests tests/test_root_password.js
-```
-
-At the first run it installs Puppeteer and the dependant NPM packages. You can
-install them manually with this command:
-
-```sh
-PUPPETEER_SKIP_DOWNLOAD=true npm install --omit=optional
-```
-
-## Options
-
-The recommended command to run the test during development is
-
-```sh
-AGAMA_BROWSER=chromium AGAMA_SERVER=https://agama.local AGAMA_SLOWMO=50 \
-AGAMA_HEADLESS=false ./agama-integration-tests tests/test_root_password.js
-```
-
-The options are described below.
-
-### Test Browser
-
-By default the test uses the Firefox browser but it is possible to use Chromium
-or Google Chrome as well. See the [supported browsers](#supported-browsers)
-section below.
-
-Set `AGAMA_BROWSER=chromium` or `AGAMA_BROWSER=chrome` to use different
-browsers.
-
-### Headless Mode
-
-The test runs in headless mode (no UI displayed). For development or debugging
-it might be better to see the real browser running the test.
-
-Set `AGAMA_HEADLESS=false` to display the browser during the test.
-
-When running the test from the Live ISO you need to enable the X forwarding
-(`ssh -X` option) or set `DISPLAY=:0` to use the locally running X server.
-
-### Target Agama Server
-
-The test connects to a locally running Agama, for using a remote server set
-the `AGAMA_SERVER` to the server URL.
-
-### Slow Motion
-
-Because the browser is controlled by a script the actions might be too fast to
-watch. Use the `AGAMA_SLOWMO` variable with a delay in miliseconds between the
-actions. A reasonable value is round 50.
-
-## Supported Browsers
-
-The Puppeteer library was originally written for the Chromium browser, but later
-they added support also for the Firefox browser. However, not all features might
-be supported in Firefox, e.g. it cannot record a video of the test run. See
-more details in the [Puppeteer documentation](https://pptr.dev/webdriver-bidi).
-
-> [!NOTE]
-Unfortunately the Firefox version installed in SLE15 and openSUSE Leap 15.x is
-too old and does not work with Puppeteer. The version in openSUSE Tumbleweed
-works fine.
diff --git a/puppeteer/agama-integration-tests b/puppeteer/agama-integration-tests
deleted file mode 100755
index 8829b86005..0000000000
--- a/puppeteer/agama-integration-tests
+++ /dev/null
@@ -1,34 +0,0 @@
-#! /usr/bin/bash
-
-# A helper script for running the Puppeteer integration tests.
-#
-# Usage:
-# agama-integration-tests [mochajs-options]
-
-# exit on error, unset variables are an error
-set -eu
-
-MYDIR=$(realpath "$(dirname "$0")")
-
-# options passed to mocha.js:
-# --bail: stop at the first failure (otherwise the test would continue and very
-# likely fail at the next steps as well, this prevents from false alarms)
-# --slow: report tests as slow when they take more than 10 seconds, the default
-# is 75ms which is too small for Agama
-MOCHA_OPTIONS=(--bail --slow 10000)
-
-if [ -e "$MYDIR/../.git/" ]; then
- npm install --omit=optional
-
- # do the same node_modules cleanup as in the RPM package to have the very same
- # environment and have consistent results
- "$MYDIR/node-prune.sh"
- "$MYDIR/node-puppeteer-prune.sh"
-
- npx mocha "${MOCHA_OPTIONS[@]}" "$@"
-else
- # set the default load path
- export NODE_PATH=/usr/share/agama/integration-tests/node_modules
- # run the CLI script directly, npm/npx might not be installed
- /usr/bin/env node /usr/share/agama/integration-tests/node_modules/mocha/bin/mocha.js "${MOCHA_OPTIONS[@]}" "$@"
-fi
diff --git a/puppeteer/node-prune.sh b/puppeteer/node-prune.sh
deleted file mode 100755
index 7770c806c3..0000000000
--- a/puppeteer/node-prune.sh
+++ /dev/null
@@ -1,159 +0,0 @@
-#! /bin/bash
-
-# This script clean up the node_modules directory which is usually huge and
-# contains a lot of not needed files. It was inspired by the node-prune tool
-# (https://github.com/tj/node-prune).
-#
-# Usage:
-#
-# node-prune.sh [path]
-#
-# The optional [path] argument is a path to the node_modules directory, if it is
-# not specified it uses node_modules in the current directory.
-#
-# This is a generic tool, you might run it against any node_modules directory,
-# not only in Agama Puppeteer tests.
-
-MODULES_PATH="${1:-./node_modules}"
-
-# The list of names/patterns comes from
-# https://github.com/tj/node-prune/blob/master/internal/prune/prune.go
-
-# files to delete
-FILES=(
- Jenkinsfile
- Makefile
- Gulpfile.js
- Gruntfile.js
- gulpfile.js
- .DS_Store
- .tern-project
- .gitattributes
- .editorconfig
- .eslintrc
- eslint
- .eslintrc.js
- .eslintrc.json
- .eslintrc.yml
- .eslintignore
- .stylelintrc
- stylelint.config.js
- .stylelintrc.json
- .stylelintrc.yaml
- .stylelintrc.yml
- .stylelintrc.js
- .htmllintrc
- htmllint.js
- .lint
- .npmrc
- .npmignore
- .jshintrc
- .flowconfig
- .documentup.json
- .yarn-metadata.json
- .travis.yml
- appveyor.yml
- .gitlab-ci.yml
- circle.yml
- .coveralls.yml
- CHANGES
- changelog
- # keep the package licenses, it's unclear if we can legally delete them...
- # LICENSE.txt
- # LICENSE
- # LICENSE-MIT
- # LICENSE.BSD
- # license
- # LICENCE.txt
- # LICENCE
- # LICENCE-MIT
- # LICENCE.BSD
- # licence
- AUTHORS
- CONTRIBUTORS
- .yarn-integrity
- .yarnclean
- _config.yml
- .babelrc
- .yo-rc.json
- jest.config.js
- karma.conf.js
- wallaby.js
- wallaby.conf.js
- .prettierrc
- .prettierrc.yml
- .prettierrc.toml
- .prettierrc.js
- .prettierrc.json
- prettier.config.js
- .appveyor.yml
- tsconfig.json
- tslint.json
-)
-
-# directories to delete
-DIRECTORIES=(
- test
- tests
- powered-test
- docs
- doc
- .idea
- .vscode
- website
- images
- assets
- example
- examples
- coverage
- .nyc_output
- .circleci
- .github
-)
-
-# delete files with specific extensions
-EXTENSIONS=(
- markdown
- md
- mkd
- ts
- jst
- coffee
- tgz
- swp
-)
-
-# delete additional files with specific extensions (not deleted by the original
-# node-prune tool)
-EXTRA_EXTENSIONS=(
- # The map files take almost half of the node_modules content! An they would be
- # useful only for reporting bugs in Puppeteer itself or in some dependent
- # library.
- map
-)
-
-echo -n "Before cleanup: "
-du -h -s "$MODULES_PATH" | cut -f1
-
-# delete files
-for F in "${FILES[@]}"; do
- find "$MODULES_PATH" -type f -name "$F" -delete
-done
-
-# delete directories recursively
-for D in "${DIRECTORIES[@]}"; do
- find "$MODULES_PATH" -type d -name "$D" -prune -exec rm -rf \{\} \;
-done
-
-# delete files with specific extenstions
-for E in "${EXTENSIONS[@]}"; do
- find "$MODULES_PATH" -type f -name "*.$E" -delete
-done
-
-# delete additional files with extensions
-for EE in "${EXTRA_EXTENSIONS[@]}"; do
- find "$MODULES_PATH" -type f -name "*.$EE" -delete
-done
-
-echo -n "After cleanup: "
-du -h -s "$MODULES_PATH" | cut -f1
diff --git a/puppeteer/node-puppeteer-prune.sh b/puppeteer/node-puppeteer-prune.sh
deleted file mode 100755
index 1737f1c92e..0000000000
--- a/puppeteer/node-puppeteer-prune.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#! /bin/sh
-
-# This is a helper script which deletes some not needed files from NPM packages.
-# This script is specific for Puppeteer installations.
-
-MODULES_PATH="${1:-./node_modules}"
-
-# delete Puppeteer CommonJS modules, we use the ES modules (in lib/esm)
-rm -rf "$MODULES_PATH/puppeteer-core/lib/cjs"
diff --git a/puppeteer/package-lock.json b/puppeteer/package-lock.json
deleted file mode 100644
index 9647e20115..0000000000
--- a/puppeteer/package-lock.json
+++ /dev/null
@@ -1,2794 +0,0 @@
-{
- "name": "puppeteer",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "dependencies": {
- "chai": "^5.1.1",
- "mocha": "^10.6.0",
- "puppeteer-core": "22.13.0"
- }
- },
- "node_modules/@puppeteer/browsers": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz",
- "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==",
- "dependencies": {
- "debug": "4.3.4",
- "extract-zip": "2.0.1",
- "progress": "2.0.3",
- "proxy-agent": "6.4.0",
- "semver": "7.6.0",
- "tar-fs": "3.0.5",
- "unbzip2-stream": "1.4.3",
- "yargs": "17.7.2"
- },
- "bin": {
- "browsers": "lib/cjs/main-cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@puppeteer/browsers/node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@puppeteer/browsers/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/@puppeteer/browsers/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "node_modules/@puppeteer/browsers/node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@puppeteer/browsers/node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@tootallnate/quickjs-emscripten": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
- "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="
- },
- "node_modules/@types/node": {
- "version": "20.14.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
- "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
- "optional": true,
- "dependencies": {
- "undici-types": "~5.26.4"
- }
- },
- "node_modules/@types/yauzl": {
- "version": "2.10.3",
- "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
- "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
- "optional": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dependencies": {
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/ansi-colors": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
- "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/ast-types": {
- "version": "0.13.4",
- "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
- "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
- "dependencies": {
- "tslib": "^2.0.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/b4a": {
- "version": "1.6.6",
- "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz",
- "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg=="
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "node_modules/bare-events": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz",
- "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==",
- "optional": true
- },
- "node_modules/bare-fs": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz",
- "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==",
- "optional": true,
- "dependencies": {
- "bare-events": "^2.0.0",
- "bare-path": "^2.0.0",
- "bare-stream": "^2.0.0"
- }
- },
- "node_modules/bare-os": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz",
- "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==",
- "optional": true
- },
- "node_modules/bare-path": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz",
- "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==",
- "optional": true,
- "dependencies": {
- "bare-os": "^2.1.0"
- }
- },
- "node_modules/bare-stream": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz",
- "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==",
- "optional": true,
- "dependencies": {
- "streamx": "^2.18.0"
- }
- },
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/basic-ftp": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
- "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
- },
- "node_modules/buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
- "node_modules/buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/chai": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
- "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/chalk/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
- "engines": {
- "node": ">= 16"
- }
- },
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/chromium-bidi": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.0.tgz",
- "integrity": "sha512-VnxVrpGojAjkiGFN2I+KtsDILFAjiGWVEDizOEnKzEDkT93eQT1cqTfUkqmOyLq33i1q4a1KDYbH+52CUe4Ufw==",
- "dependencies": {
- "mitt": "3.0.1",
- "urlpattern-polyfill": "10.0.0",
- "zod": "3.23.8"
- },
- "peerDependencies": {
- "devtools-protocol": "*"
- }
- },
- "node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "node_modules/data-uri-to-buffer": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
- "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/debug": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
- "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/debug/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "node_modules/decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/degenerator": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
- "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
- "dependencies": {
- "ast-types": "^0.13.4",
- "escodegen": "^2.1.0",
- "esprima": "^4.0.1"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/devtools-protocol": {
- "version": "0.0.1299070",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz",
- "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg=="
- },
- "node_modules/diff": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
- "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "node_modules/end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
- "node_modules/escalade": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
- "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "dependencies": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2"
- },
- "bin": {
- "escodegen": "bin/escodegen.js",
- "esgenerate": "bin/esgenerate.js"
- },
- "engines": {
- "node": ">=6.0"
- },
- "optionalDependencies": {
- "source-map": "~0.6.1"
- }
- },
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/extract-zip": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
- "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
- "dependencies": {
- "debug": "^4.1.1",
- "get-stream": "^5.1.0",
- "yauzl": "^2.10.0"
- },
- "bin": {
- "extract-zip": "cli.js"
- },
- "engines": {
- "node": ">= 10.17.0"
- },
- "optionalDependencies": {
- "@types/yauzl": "^2.9.1"
- }
- },
- "node_modules/fast-fifo": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
- "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
- },
- "node_modules/fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "dependencies": {
- "pend": "~1.2.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
- "bin": {
- "flat": "cli.js"
- }
- },
- "node_modules/fs-extra": {
- "version": "11.2.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
- "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/get-func-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
- "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/get-uri": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz",
- "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==",
- "dependencies": {
- "basic-ftp": "^5.0.2",
- "data-uri-to-buffer": "^6.0.2",
- "debug": "^4.3.4",
- "fs-extra": "^11.2.0"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "bin": {
- "he": "bin/he"
- }
- },
- "node_modules/http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "node_modules/ip-address": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
- "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
- "dependencies": {
- "jsbn": "1.1.0",
- "sprintf-js": "^1.1.3"
- },
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/jsbn": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
- "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
- },
- "node_modules/jsonfile": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
- "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "dependencies": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/loupe": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz",
- "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==",
- "dependencies": {
- "get-func-name": "^2.0.1"
- }
- },
- "node_modules/lru-cache": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
- "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/mitt": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
- "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
- },
- "node_modules/mocha": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz",
- "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==",
- "dependencies": {
- "ansi-colors": "^4.1.3",
- "browser-stdout": "^1.3.1",
- "chokidar": "^3.5.3",
- "debug": "^4.3.5",
- "diff": "^5.2.0",
- "escape-string-regexp": "^4.0.0",
- "find-up": "^5.0.0",
- "glob": "^8.1.0",
- "he": "^1.2.0",
- "js-yaml": "^4.1.0",
- "log-symbols": "^4.1.0",
- "minimatch": "^5.1.6",
- "ms": "^2.1.3",
- "serialize-javascript": "^6.0.2",
- "strip-json-comments": "^3.1.1",
- "supports-color": "^8.1.1",
- "workerpool": "^6.5.1",
- "yargs": "^16.2.0",
- "yargs-parser": "^20.2.9",
- "yargs-unparser": "^2.0.0"
- },
- "bin": {
- "_mocha": "bin/_mocha",
- "mocha": "bin/mocha.js"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
- "node_modules/netmask": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
- "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/pac-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==",
- "dependencies": {
- "@tootallnate/quickjs-emscripten": "^0.23.0",
- "agent-base": "^7.0.2",
- "debug": "^4.3.4",
- "get-uri": "^6.0.1",
- "http-proxy-agent": "^7.0.0",
- "https-proxy-agent": "^7.0.5",
- "pac-resolver": "^7.0.1",
- "socks-proxy-agent": "^8.0.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/pac-resolver": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
- "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
- "dependencies": {
- "degenerator": "^5.0.0",
- "netmask": "^2.0.2"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/pathval": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
- "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
- "engines": {
- "node": ">= 14.16"
- }
- },
- "node_modules/pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/proxy-agent": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
- "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "^4.3.4",
- "http-proxy-agent": "^7.0.1",
- "https-proxy-agent": "^7.0.3",
- "lru-cache": "^7.14.1",
- "pac-proxy-agent": "^7.0.1",
- "proxy-from-env": "^1.1.0",
- "socks-proxy-agent": "^8.0.2"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "node_modules/pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/puppeteer-core": {
- "version": "22.13.0",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.13.0.tgz",
- "integrity": "sha512-ZkpRX8nm/S39BnpcCverMzIc6oGWBPOUeOeaWRLKHqiKVCZ1l28HxPTYLitJlDiB16xZATSKpjul+sl+ZEm0HQ==",
- "dependencies": {
- "@puppeteer/browsers": "2.2.3",
- "chromium-bidi": "0.6.0",
- "debug": "^4.3.5",
- "devtools-protocol": "0.0.1299070",
- "ws": "^8.18.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/queue-tick": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
- "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
- },
- "node_modules/randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dependencies": {
- "safe-buffer": "^5.1.0"
- }
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dependencies": {
- "randombytes": "^2.1.0"
- }
- },
- "node_modules/smart-buffer": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
- "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
- "engines": {
- "node": ">= 6.0.0",
- "npm": ">= 3.0.0"
- }
- },
- "node_modules/socks": {
- "version": "2.8.3",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
- "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
- "dependencies": {
- "ip-address": "^9.0.5",
- "smart-buffer": "^4.2.0"
- },
- "engines": {
- "node": ">= 10.0.0",
- "npm": ">= 3.0.0"
- }
- },
- "node_modules/socks-proxy-agent": {
- "version": "8.0.4",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
- "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
- "dependencies": {
- "agent-base": "^7.1.1",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "optional": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/sprintf-js": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
- "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
- },
- "node_modules/streamx": {
- "version": "2.18.0",
- "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz",
- "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==",
- "dependencies": {
- "fast-fifo": "^1.3.2",
- "queue-tick": "^1.0.1",
- "text-decoder": "^1.1.0"
- },
- "optionalDependencies": {
- "bare-events": "^2.2.0"
- }
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
- "node_modules/tar-fs": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz",
- "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==",
- "dependencies": {
- "pump": "^3.0.0",
- "tar-stream": "^3.1.5"
- },
- "optionalDependencies": {
- "bare-fs": "^2.1.1",
- "bare-path": "^2.1.0"
- }
- },
- "node_modules/tar-stream": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
- "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
- "dependencies": {
- "b4a": "^1.6.4",
- "fast-fifo": "^1.2.0",
- "streamx": "^2.15.0"
- }
- },
- "node_modules/text-decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz",
- "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==",
- "dependencies": {
- "b4a": "^1.6.4"
- }
- },
- "node_modules/through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/tslib": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
- "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
- },
- "node_modules/unbzip2-stream": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
- "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
- "dependencies": {
- "buffer": "^5.2.1",
- "through": "^2.3.8"
- }
- },
- "node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "optional": true
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/urlpattern-polyfill": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz",
- "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg=="
- },
- "node_modules/workerpool": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
- "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA=="
- },
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
- },
- "node_modules/ws": {
- "version": "8.18.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
- "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
- "node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs-unparser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
- "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
- "dependencies": {
- "camelcase": "^6.0.0",
- "decamelize": "^4.0.0",
- "flat": "^5.0.2",
- "is-plain-obj": "^2.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "dependencies": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/zod": {
- "version": "3.23.8",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
- "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
- }
- },
- "dependencies": {
- "@puppeteer/browsers": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz",
- "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==",
- "requires": {
- "debug": "4.3.4",
- "extract-zip": "2.0.1",
- "progress": "2.0.3",
- "proxy-agent": "6.4.0",
- "semver": "7.6.0",
- "tar-fs": "3.0.5",
- "unbzip2-stream": "1.4.3",
- "yargs": "17.7.2"
- },
- "dependencies": {
- "cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- }
- },
- "debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "requires": {
- "ms": "2.1.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "requires": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- }
- },
- "yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
- }
- }
- },
- "@tootallnate/quickjs-emscripten": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
- "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="
- },
- "@types/node": {
- "version": "20.14.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
- "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
- "optional": true,
- "requires": {
- "undici-types": "~5.26.4"
- }
- },
- "@types/yauzl": {
- "version": "2.10.3",
- "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
- "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
- "optional": true,
- "requires": {
- "@types/node": "*"
- }
- },
- "agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "requires": {
- "debug": "^4.3.4"
- }
- },
- "ansi-colors": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
- "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="
- },
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
- },
- "assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="
- },
- "ast-types": {
- "version": "0.13.4",
- "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
- "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
- "requires": {
- "tslib": "^2.0.1"
- }
- },
- "b4a": {
- "version": "1.6.6",
- "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz",
- "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg=="
- },
- "balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "bare-events": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz",
- "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==",
- "optional": true
- },
- "bare-fs": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz",
- "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==",
- "optional": true,
- "requires": {
- "bare-events": "^2.0.0",
- "bare-path": "^2.0.0",
- "bare-stream": "^2.0.0"
- }
- },
- "bare-os": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz",
- "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==",
- "optional": true
- },
- "bare-path": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz",
- "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==",
- "optional": true,
- "requires": {
- "bare-os": "^2.1.0"
- }
- },
- "bare-stream": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz",
- "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==",
- "optional": true,
- "requires": {
- "streamx": "^2.18.0"
- }
- },
- "base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
- },
- "basic-ftp": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
- "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="
- },
- "binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
- },
- "brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "requires": {
- "balanced-match": "^1.0.0"
- }
- },
- "braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "requires": {
- "fill-range": "^7.1.1"
- }
- },
- "browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
- },
- "buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "requires": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
- "buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
- },
- "camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
- },
- "chai": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
- "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
- "requires": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "dependencies": {
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "check-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
- "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="
- },
- "chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "requires": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "fsevents": "~2.3.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- }
- },
- "chromium-bidi": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.0.tgz",
- "integrity": "sha512-VnxVrpGojAjkiGFN2I+KtsDILFAjiGWVEDizOEnKzEDkT93eQT1cqTfUkqmOyLq33i1q4a1KDYbH+52CUe4Ufw==",
- "requires": {
- "mitt": "3.0.1",
- "urlpattern-polyfill": "10.0.0",
- "zod": "3.23.8"
- }
- },
- "cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "data-uri-to-buffer": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
- "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="
- },
- "debug": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
- "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
- "requires": {
- "ms": "2.1.2"
- },
- "dependencies": {
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
- }
- },
- "decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ=="
- },
- "deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="
- },
- "degenerator": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
- "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
- "requires": {
- "ast-types": "^0.13.4",
- "escodegen": "^2.1.0",
- "esprima": "^4.0.1"
- }
- },
- "devtools-protocol": {
- "version": "0.0.1299070",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz",
- "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg=="
- },
- "diff": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
- "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "requires": {
- "once": "^1.4.0"
- }
- },
- "escalade": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
- "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA=="
- },
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
- },
- "escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "requires": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2",
- "source-map": "~0.6.1"
- }
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
- },
- "estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
- },
- "esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
- },
- "extract-zip": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
- "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
- "requires": {
- "@types/yauzl": "^2.9.1",
- "debug": "^4.1.1",
- "get-stream": "^5.1.0",
- "yauzl": "^2.10.0"
- }
- },
- "fast-fifo": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
- "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
- },
- "fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "requires": {
- "pend": "~1.2.0"
- }
- },
- "fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "requires": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
- },
- "fs-extra": {
- "version": "11.2.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
- "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
- "requires": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
- },
- "fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "optional": true
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
- },
- "get-func-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
- "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="
- },
- "get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "get-uri": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz",
- "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==",
- "requires": {
- "basic-ftp": "^5.0.2",
- "data-uri-to-buffer": "^6.0.2",
- "debug": "^4.3.4",
- "fs-extra": "^11.2.0"
- }
- },
- "glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- }
- },
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
- },
- "http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "requires": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- }
- },
- "https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "requires": {
- "agent-base": "^7.0.2",
- "debug": "4"
- }
- },
- "ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "ip-address": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
- "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
- "requires": {
- "jsbn": "1.1.0",
- "sprintf-js": "^1.1.3"
- }
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
- },
- "is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
- },
- "is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="
- },
- "is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "jsbn": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
- "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
- },
- "jsonfile": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
- "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
- "requires": {
- "graceful-fs": "^4.1.6",
- "universalify": "^2.0.0"
- }
- },
- "locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "requires": {
- "p-locate": "^5.0.0"
- }
- },
- "log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "requires": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- }
- },
- "loupe": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz",
- "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==",
- "requires": {
- "get-func-name": "^2.0.1"
- }
- },
- "lru-cache": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
- "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="
- },
- "minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "requires": {
- "brace-expansion": "^2.0.1"
- }
- },
- "mitt": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
- "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
- },
- "mocha": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz",
- "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==",
- "requires": {
- "ansi-colors": "^4.1.3",
- "browser-stdout": "^1.3.1",
- "chokidar": "^3.5.3",
- "debug": "^4.3.5",
- "diff": "^5.2.0",
- "escape-string-regexp": "^4.0.0",
- "find-up": "^5.0.0",
- "glob": "^8.1.0",
- "he": "^1.2.0",
- "js-yaml": "^4.1.0",
- "log-symbols": "^4.1.0",
- "minimatch": "^5.1.6",
- "ms": "^2.1.3",
- "serialize-javascript": "^6.0.2",
- "strip-json-comments": "^3.1.1",
- "supports-color": "^8.1.1",
- "workerpool": "^6.5.1",
- "yargs": "^16.2.0",
- "yargs-parser": "^20.2.9",
- "yargs-unparser": "^2.0.0"
- }
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
- "netmask": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
- "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "requires": {
- "wrappy": "1"
- }
- },
- "p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "requires": {
- "yocto-queue": "^0.1.0"
- }
- },
- "p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "requires": {
- "p-limit": "^3.0.2"
- }
- },
- "pac-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==",
- "requires": {
- "@tootallnate/quickjs-emscripten": "^0.23.0",
- "agent-base": "^7.0.2",
- "debug": "^4.3.4",
- "get-uri": "^6.0.1",
- "http-proxy-agent": "^7.0.0",
- "https-proxy-agent": "^7.0.5",
- "pac-resolver": "^7.0.1",
- "socks-proxy-agent": "^8.0.4"
- }
- },
- "pac-resolver": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
- "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
- "requires": {
- "degenerator": "^5.0.0",
- "netmask": "^2.0.2"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
- },
- "pathval": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
- "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA=="
- },
- "pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
- },
- "picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
- },
- "progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
- },
- "proxy-agent": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
- "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
- "requires": {
- "agent-base": "^7.0.2",
- "debug": "^4.3.4",
- "http-proxy-agent": "^7.0.1",
- "https-proxy-agent": "^7.0.3",
- "lru-cache": "^7.14.1",
- "pac-proxy-agent": "^7.0.1",
- "proxy-from-env": "^1.1.0",
- "socks-proxy-agent": "^8.0.2"
- }
- },
- "proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "puppeteer-core": {
- "version": "22.13.0",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.13.0.tgz",
- "integrity": "sha512-ZkpRX8nm/S39BnpcCverMzIc6oGWBPOUeOeaWRLKHqiKVCZ1l28HxPTYLitJlDiB16xZATSKpjul+sl+ZEm0HQ==",
- "requires": {
- "@puppeteer/browsers": "2.2.3",
- "chromium-bidi": "0.6.0",
- "debug": "^4.3.5",
- "devtools-protocol": "0.0.1299070",
- "ws": "^8.18.0"
- }
- },
- "queue-tick": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
- "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
- },
- "randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "requires": {
- "safe-buffer": "^5.1.0"
- }
- },
- "readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "requires": {
- "picomatch": "^2.2.1"
- }
- },
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
- },
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
- },
- "semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "requires": {
- "lru-cache": "^6.0.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "requires": {
- "yallist": "^4.0.0"
- }
- }
- }
- },
- "serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "requires": {
- "randombytes": "^2.1.0"
- }
- },
- "smart-buffer": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
- "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
- },
- "socks": {
- "version": "2.8.3",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
- "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
- "requires": {
- "ip-address": "^9.0.5",
- "smart-buffer": "^4.2.0"
- }
- },
- "socks-proxy-agent": {
- "version": "8.0.4",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
- "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
- "requires": {
- "agent-base": "^7.1.1",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "optional": true
- },
- "sprintf-js": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
- "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
- },
- "streamx": {
- "version": "2.18.0",
- "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz",
- "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==",
- "requires": {
- "bare-events": "^2.2.0",
- "fast-fifo": "^1.3.2",
- "queue-tick": "^1.0.1",
- "text-decoder": "^1.1.0"
- }
- },
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
- "strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
- },
- "supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "tar-fs": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz",
- "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==",
- "requires": {
- "bare-fs": "^2.1.1",
- "bare-path": "^2.1.0",
- "pump": "^3.0.0",
- "tar-stream": "^3.1.5"
- }
- },
- "tar-stream": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
- "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
- "requires": {
- "b4a": "^1.6.4",
- "fast-fifo": "^1.2.0",
- "streamx": "^2.15.0"
- }
- },
- "text-decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz",
- "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==",
- "requires": {
- "b4a": "^1.6.4"
- }
- },
- "through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "tslib": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
- "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
- },
- "unbzip2-stream": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
- "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
- "requires": {
- "buffer": "^5.2.1",
- "through": "^2.3.8"
- }
- },
- "undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "optional": true
- },
- "universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="
- },
- "urlpattern-polyfill": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz",
- "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg=="
- },
- "workerpool": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
- "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA=="
- },
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
- },
- "ws": {
- "version": "8.18.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
- "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
- "requires": {}
- },
- "y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
- },
- "yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
- "yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "requires": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- }
- },
- "yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
- },
- "yargs-unparser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
- "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
- "requires": {
- "camelcase": "^6.0.0",
- "decamelize": "^4.0.0",
- "flat": "^5.0.2",
- "is-plain-obj": "^2.1.0"
- }
- },
- "yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "requires": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- },
- "yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
- },
- "zod": {
- "version": "3.23.8",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
- "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="
- }
- }
-}
diff --git a/puppeteer/package.json b/puppeteer/package.json
deleted file mode 100644
index 71c5178aa1..0000000000
--- a/puppeteer/package.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "type": "module",
- "dependencies": {
- "chai": "^5.1.1",
- "mocha": "^10.6.0",
- "puppeteer-core": "22.13.0"
- }
-}
diff --git a/puppeteer/package/_service b/puppeteer/package/_service
deleted file mode 100644
index f11ca977ba..0000000000
--- a/puppeteer/package/_service
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- @PARENT_TAG@+@TAG_OFFSET@
- v(.*)
-
- https://github.com/agama-project/agama.git
- git
-
- master
- puppeteer
- enable
- package-lock.json
- package/agama-integration-tests.changes
- package/agama-integration-tests.spec
-
-
- node_modules.obscpio
- node_modules.spec.inc
- 1000
-
-
- agama.obsinfo
- agama
-
-
- agama
-
-
diff --git a/puppeteer/package/agama-integration-tests.changes b/puppeteer/package/agama-integration-tests.changes
deleted file mode 100644
index 2a0a0330b5..0000000000
--- a/puppeteer/package/agama-integration-tests.changes
+++ /dev/null
@@ -1,22 +0,0 @@
--------------------------------------------------------------------
-Fri Sep 20 11:29:10 UTC 2024 - Imobach Gonzalez Sosa
-
-- Change the license to GPL-2.0-or-later (gh#openSUSE/agama#1621).
-
--------------------------------------------------------------------
-Wed Jul 24 15:03:25 UTC 2024 - Ladislav Slezák
-
-- Reduce the node_modules size, delete not needed files
- (gh#openSUSE/agama#1497)
-
--------------------------------------------------------------------
-Fri Jul 19 10:18:37 UTC 2024 - Ladislav Slezák
-
-- Downgrade Puppeteer from 22.13.1 to 22.13.0, the latest version
- fails to start with the Firefox browser (gh#openSUSE/agama#1485)
-
--------------------------------------------------------------------
-Tue Jul 16 13:25:52 UTC 2024 - Ladislav Slezák
-
-- Initial version
-
diff --git a/puppeteer/package/agama-integration-tests.spec b/puppeteer/package/agama-integration-tests.spec
deleted file mode 100644
index 931f436f02..0000000000
--- a/puppeteer/package/agama-integration-tests.spec
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# spec file for package agama-integration-tests
-#
-# Copyright (c) 2024 SUSE LLC
-#
-# All modifications and additions to the file contributed by third parties
-# remain the property of their copyright owners, unless otherwise agreed
-# upon. The license for this file, and modifications and additions to the
-# file, is the same license as for the pristine package itself (unless the
-# license for the pristine package is not an Open Source License, in which
-# case the license is the MIT License). An "Open Source License" is a
-# license that conforms to the Open Source Definition (Version 1.9)
-# published by the Open Source Initiative.
-
-# Please submit bugfixes or comments via https://bugs.opensuse.org/
-#
-
-Name: agama-integration-tests
-Version: 0
-Release: 0
-Summary: Support for running Agama integration tests
-License: GPL-2.0-or-later
-URL: https://github.com/openSUSE/agama
-# source_validator insists that if obscpio has no version then
-# tarball must neither
-Source0: agama.tar
-Source10: package-lock.json
-Source11: node_modules.spec.inc
-Source12: node_modules.sums
-%include %_sourcedir/node_modules.spec.inc
-BuildArch: noarch
-BuildRequires: fdupes
-BuildRequires: local-npm-registry
-Requires: nodejs(engine) >= 18
-
-%description
-This package provides infrastructure and tooling needed to run the Agama
-integration tests. It includes the Puppeteer framework with all dependencies.
-
-The package includes only one example test, the real tests should be added from
-outside.
-
-%prep
-%autosetup -p1 -n agama
-
-%build
-rm -f package-lock.json
-local-npm-registry %{_sourcedir} install --omit=optional --with=dev --legacy-peer-deps || ( find ~/.npm/_logs -name '*-debug.log' -print0 | xargs -0 cat; false)
-
-# node_modules cleanup
-%{_builddir}/agama/node-prune.sh
-
-# extra cleanup for the Puppeteer NPM packages
-%{_builddir}/agama/node-puppeteer-prune.sh
-
-%install
-install -D -d -m 0755 %{buildroot}%{_datadir}/agama/integration-tests
-cp -aR node_modules %{buildroot}%{_datadir}/agama/integration-tests
-cp -aR %{_builddir}/agama/tests %{buildroot}%{_datadir}/agama/integration-tests
-cp -a %{_builddir}/agama/package.json %{buildroot}%{_datadir}/agama/integration-tests
-install -D -d -m 0755 %{buildroot}%{_bindir}
-cp -a %{_builddir}/agama/agama-integration-tests %{buildroot}%{_bindir}
-
-rm %{buildroot}%{_datadir}/agama/integration-tests/node_modules/.package-lock.json
-
-# symlink duplicate files
-%fdupes -s %{buildroot}/%{_datadir}/agama/integration-tests
-
-%files
-%defattr(-,root,root,-)
-%doc README.md
-%license LICENSE
-%dir %{_datadir}/agama
-%{_datadir}/agama/integration-tests
-%attr(0755,root,root) %{_bindir}/agama-integration-tests
-
-%changelog
diff --git a/puppeteer/tests/test_root_password.js b/puppeteer/tests/test_root_password.js
deleted file mode 100644
index cdb7c5cc22..0000000000
--- a/puppeteer/tests/test_root_password.js
+++ /dev/null
@@ -1,197 +0,0 @@
-import fs from "fs";
-import path from "path";
-
-import puppeteer from "puppeteer-core";
-import { expect } from "chai";
-
-// This is an example file for running Agama integration tests using Puppeteer.
-//
-// If the test fails it saves the page screenshot and the HTML page dump to
-// ./log/ subdirectory.
-// For more details about customization see the README.md file.
-
-// helper function for converting String to Boolean
-function booleanEnv(name, default_value) {
- const env = process.env[name];
- if (env === undefined) {
- return default_value;
- }
- switch (env.toLowerCase()) {
- case "0":
- case "false":
- case "off":
- case "disabled":
- case "no":
- return false;
- case "1":
- case "true":
- case "on":
- case "enabled":
- case "yes":
- return true;
- default:
- return default_value;
- }
-}
-
-// helper function for configuring the browser
-function browserSettings(name) {
- switch (name.toLowerCase()) {
- case "firefox":
- return {
- product: "firefox",
- executablePath: "/usr/bin/firefox",
- };
- case "chrome":
- return {
- product: "chrome",
- executablePath: "/usr/bin/google-chrome-stable",
- };
- case "chromium":
- return {
- product: "chrome",
- executablePath: "/usr/bin/chromium",
- };
- default:
- throw new Error(`Unsupported browser type: ${name}`);
- }
-}
-
-const agamaServer = process.env.AGAMA_SERVER || "http://localhost";
-const agamaPassword = process.env.AGAMA_PASSWORD || "linux";
-const agamaBrowser = process.env.AGAMA_BROWSER || "firefox";
-const slowMo = parseInt(process.env.AGAMA_SLOWMO || "0");
-const headless = booleanEnv("AGAMA_HEADLESS", true);
-
-describe("Agama test", function () {
- // mocha timeout
- this.timeout(20000);
-
- let page;
- let browser;
-
- before(async function () {
- browser = await puppeteer.launch({
- // "webDriverBiDi" does not work with old FireFox, comment it out if needed
- protocol: "webDriverBiDi",
- headless,
- ignoreHTTPSErrors: true,
- timeout: 30000,
- slowMo,
- defaultViewport: {
- width: 1280,
- height: 768,
- },
- ...browserSettings(agamaBrowser),
- });
- page = await browser.newPage();
- page.setDefaultTimeout(20000);
- await page.goto(agamaServer, {
- timeout: 60000,
- waitUntil: "domcontentloaded",
- });
- });
-
- after(async function () {
- await page.close();
- await browser.close();
- });
-
- // automatically take a screenshot and dump the page content for failed tests
- afterEach(async function () {
- if (this.currentTest.state === "failed") {
- // directory for storing the data
- const dir = "log";
- if (!fs.existsSync(dir)) fs.mkdirSync(dir);
-
- // base file name for the dumps
- const name = path.join(dir, this.currentTest.title.replace(/[^a-zA-Z0-9]/g, "_"));
- await page.screenshot({ path: name + ".png" });
- const html = await page.content();
- fs.writeFileSync(name + ".html", html);
- }
- });
-
- it("should have Agama page title", async function () {
- expect(await page.title()).to.eql("Agama");
- });
-
- it("allows logging in", async function () {
- // await page.waitForSelector("input#password");
- await page.type("input#password", agamaPassword);
- await page.click("button[type='submit']");
- });
-
- it("should optionally display the product selection dialog", async function () {
- // Either the root password setting is displayed or there is
- // the product selection page.
- const productSelectionDisplayed = await Promise.any([
- page.waitForSelector("input#rootPassword").then((s) => {
- s.dispose();
- return false;
- }),
- page.waitForSelector("button[form='productSelectionForm']").then((s) => {
- s.dispose();
- return true;
- }),
- ]);
-
- if (productSelectionDisplayed) {
- const product = await page.locator("label[for='opensuse-tumbleweed']").waitHandle();
- // scroll the page so the product is visible
- await product.scrollIntoView();
- await product.click();
-
- await page
- .locator("button[form='productSelectionForm']")
- // wait until the button is enabled
- .setWaitForEnabled(true)
- .click();
- } else {
- // no product selection displayed, mark the test as skipped
- this.skip();
- }
- });
-
- it("should require setting the root password", async function () {
- // increase the timeout for the whole test
- this.timeout(60000);
- await page
- .locator("input#rootPassword")
- // refreshing the repositories before showing the password configuration might take long time
- .setTimeout(60000)
- .waitHandle()
- // type the new password
- .then((h) => h.type("test"));
- await page.locator("button[type='submit']").setWaitForEnabled(true).click();
- });
-
- it("should display overview card", async function () {
- await page.waitForSelector("h3::-p-text('Overview')");
- });
-
- it("should allow setting the root password", async function () {
- await page.locator("a[href='#/users']").click();
-
- const button = await Promise.any([
- page.waitForSelector("button::-p-text(Set a password)"),
- page.waitForSelector("button#actions-for-root-password"),
- ]);
-
- await button.click();
- const id = await button.evaluate((x) => x.id);
- // drop the handler to avoid memory leaks
- button.dispose();
-
- // if the menu button was clicked we need to additionally press the "Change" menu item
- if (id === "actions-for-root-password") {
- await page.locator("button[role='menuitem']::-p-text('Change')").click();
- }
-
- const newPassword = "test";
- await page.type("input#password", newPassword);
- await page.type("input#passwordConfirmation", newPassword);
-
- await page.locator("button::-p-text(Confirm)").click();
- });
-});
From 6fd4bda0a2921a637a929f6b45f55b41bef5177a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Tue, 7 Jan 2025 09:51:58 +0000
Subject: [PATCH 077/147] fix(web): please TypeScript
---
web/src/components/product/ProductSelectionProgress.test.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/src/components/product/ProductSelectionProgress.test.tsx b/web/src/components/product/ProductSelectionProgress.test.tsx
index 082ac388d2..0171fe9bc0 100644
--- a/web/src/components/product/ProductSelectionProgress.test.tsx
+++ b/web/src/components/product/ProductSelectionProgress.test.tsx
@@ -30,7 +30,7 @@ import { Product } from "~/types/software";
jest.mock("~/components/core/ProgressReport", () => () =>
ProgressReport Mock
);
let isBusy = false;
-const tumbleweed: Product = { id: "openSUSE", name: "openSUSE Tumbleweed" };
+const tumbleweed: Product = { id: "openSUSE", name: "openSUSE Tumbleweed", registration: "no" };
jest.mock("~/queries/status", () => ({
...jest.requireActual("~/queries/status"),
From 6d0919ea5d0868f9408943f57441616497479ad6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Tue, 7 Jan 2025 09:52:20 +0000
Subject: [PATCH 078/147] fix(web): do not show the registration alert when
registration type unknonw
The registration attribute is actually mandatory, but better to prevent
displaying the alert if it is missing for some reason.
---
web/src/components/product/ProductRegistrationAlert.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/src/components/product/ProductRegistrationAlert.tsx b/web/src/components/product/ProductRegistrationAlert.tsx
index 4f09c30229..4693a969db 100644
--- a/web/src/components/product/ProductRegistrationAlert.tsx
+++ b/web/src/components/product/ProductRegistrationAlert.tsx
@@ -50,7 +50,7 @@ export default function ProductRegistrationAlert() {
// NOTE: it shouldn't be mounted in these paths, but let's prevent rendering
// if so just in case.
if (SUPPORTIVE_PATHS.includes(location.pathname)) return;
- if (product.registration === "no" || !isEmpty(registration.key)) return;
+ if (["no", undefined].includes(product.registration) || !isEmpty(registration.key)) return;
return (
From 9e7f3d318f02b044d337ed93b3c359d9ae713c07 Mon Sep 17 00:00:00 2001
From: Lubos Kocman
Date: Tue, 7 Jan 2025 13:58:01 +0100
Subject: [PATCH 079/147] Drop yast from Leap 16 selection
- Related to code-o-o#leap/features#173
---
products.d/agama-products.changes | 7 +++++++
products.d/leap_160.yaml | 3 ---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/products.d/agama-products.changes b/products.d/agama-products.changes
index 3c0380ef27..06827e89e8 100644
--- a/products.d/agama-products.changes
+++ b/products.d/agama-products.changes
@@ -1,3 +1,10 @@
+-------------------------------------------------------------------
+Tue Jan 7 12:57:13 UTC 2025 - Lubos Kocman
+
+- Drop yast from Leap 16.0 software selection
+ code-o-o#leap/features#173
+
+
-------------------------------------------------------------------
Mon Jan 6 14:41:28 UTC 2025 - Angela Briel
diff --git a/products.d/leap_160.yaml b/products.d/leap_160.yaml
index 273b72ee9a..0c9563becc 100644
--- a/products.d/leap_160.yaml
+++ b/products.d/leap_160.yaml
@@ -50,9 +50,6 @@ software:
- basic_desktop
- gnome
- kde
- - yast2_basis
- - yast2_desktop
- - yast2_server
- multimedia
- office
mandatory_packages:
From 9aedc04976eefb2d42c9a3f72e18c91a0b775a58 Mon Sep 17 00:00:00 2001
From: Knut Anderssen
Date: Tue, 7 Jan 2025 10:18:35 +0000
Subject: [PATCH 080/147] Initial implementation for agama cmdline conf
---
.../systemd/system/agama-cmdline-env.service | 17 ++++++++++
live/root/usr/bin/agama-cmdline-env | 24 ++++++++++++++
.../dracut/modules.d/99agama-cmdline/README | 6 ++++
.../99agama-cmdline/agama-cmdline-conf.sh | 32 +++++++++++++++++++
.../modules.d/99agama-cmdline/module-setup.sh | 21 ++++++++++++
.../99agama-cmdline/save-agama-conf.sh | 11 +++++++
6 files changed, 111 insertions(+)
create mode 100644 live/root/etc/systemd/system/agama-cmdline-env.service
create mode 100755 live/root/usr/bin/agama-cmdline-env
create mode 100644 live/root/usr/lib/dracut/modules.d/99agama-cmdline/README
create mode 100755 live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
create mode 100755 live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh
create mode 100755 live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh
diff --git a/live/root/etc/systemd/system/agama-cmdline-env.service b/live/root/etc/systemd/system/agama-cmdline-env.service
new file mode 100644
index 0000000000..096d5b5df4
--- /dev/null
+++ b/live/root/etc/systemd/system/agama-cmdline-env.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=Export the configuration from kernel command line as environment variables
+
+# before starting the Agama server
+Before=agama-web-server.service
+Before=agama.service
+
+# before the interactive setting methods so they can override it
+Before=live-password-dialog.service
+Before=live-password-systemd.service
+
+[Service]
+ExecStart=source /usr/bin/agama-cmdline-env
+Type=oneshot
+
+[Install]
+WantedBy=default.target
diff --git a/live/root/usr/bin/agama-cmdline-env b/live/root/usr/bin/agama-cmdline-env
new file mode 100755
index 0000000000..4e4dded3b9
--- /dev/null
+++ b/live/root/usr/bin/agama-cmdline-env
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+#----[ import_agama_env ]----#
+function import_agama_env () {
+#--------------------------------------------------
+# import agama cmdline information as environment
+# variables to the current environment
+# ---
+ TERM_SAVE=$TERM
+ if [ -f /etc/agama.d/cmdline.conf ]; then
+ IFS_SAVE=$IFS
+IFS="
+"
+ for i in `cat /etc/agama.d/cmdline.conf | sed -e s'@=@%@'`;do
+ varname=`echo $i | cut -f 1 -d% | tr -d " " | sed -e s'@\.@_@'`
+ varvals=`echo $i | cut -f 2 -d%`
+ varvals=`echo $varvals | sed -e s'@^ *@@' -e s'@ *$@@'`
+ export $varname=$varvals
+ done
+ IFS=$IFS_SAVE
+ fi
+}
+
+import_agama_env
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/README b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/README
new file mode 100644
index 0000000000..2787db46c2
--- /dev/null
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/README
@@ -0,0 +1,6 @@
+dracut agama-cmdline module
+-------------------------------
+
+This module writes any agama configuration given through the kernel cmdline
+to its own cmdline conf file copying it to the sysroot.
+
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
new file mode 100755
index 0000000000..f006ca30f1
--- /dev/null
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
@@ -0,0 +1,32 @@
+#! /bin/sh
+
+[ -e /dracut-state.sh ] && . /dracut-state.sh
+
+. /lib/dracut-lib.sh
+
+get_agama_args() {
+ local _i _found
+
+ for _i in $CMDLINE; do
+ case $_i in
+ LIBSTORAGE_* | YAST_* | agama* | Y2* | ZYPP_*)
+ _found=1
+ ;;
+ esac
+
+ if [ -n "$_found" ]; then
+ printf "Agama variable found ($_i)"
+ printf $_i
+ if ! strstr "$_i" "="; then
+ # Set the variable as a boolean if there is no assignation
+ _i="${_i}=1"
+ fi
+ echo $_i >>/etc/cmdline.d/99-agama-cmdline.conf
+ fi
+ unset _found
+ done
+
+ return 0
+}
+
+get_agama_args
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh
new file mode 100755
index 0000000000..d957ddab0e
--- /dev/null
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# called by dracut
+check() {
+ return 0
+}
+
+# called by dracut
+depends() {
+ return 0
+}
+
+installkernel() {
+ return 0
+}
+
+# called by dracut
+install() {
+ inst_hook cmdline 99 "$moddir/agama-cmdline-conf.sh"
+ inst_hook pre-pivot 99 "$moddir/save-agama-conf.sh"
+}
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh
new file mode 100755
index 0000000000..99a328b7ee
--- /dev/null
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+[ -e /dracut-state.sh ] && . /dracut-state.sh
+
+. /lib/dracut-lib.sh
+
+if [ -e /etc/cmdline.d/99-agama-cmdline.conf ]; then
+ echo "Creating agama conf"
+ mkdir -p "$NEWROOT/etc/agama.d"
+ cp /etc/cmdline.d/99-agama-cmdline.conf "$NEWROOT/etc/agama.d/cmdline.conf"
+fi
From 57054c91e97cc5c4f81afe13319e5be1b1b81ad1 Mon Sep 17 00:00:00 2001
From: Knut Anderssen
Date: Wed, 8 Jan 2025 08:26:18 +0000
Subject: [PATCH 081/147] Enable agama-cmdline service
---
live/src/config.sh | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/live/src/config.sh b/live/src/config.sh
index d326a0c76b..9a991eb07e 100644
--- a/live/src/config.sh
+++ b/live/src/config.sh
@@ -16,7 +16,7 @@ suseSetupProduct
DISTRO=$(grep "^NAME" /etc/os-release | cut -f2 -d\= | tr -d '"' | tr " " "_")
REPO="/etc/zypp/repos.d/agama-${DISTRO}.repo"
if [ -f "${REPO}.disabled" ]; then
- mv "${REPO}.disabled" $REPO
+ mv "${REPO}.disabled" $REPO
fi
rm /etc/zypp/repos.d/*.disabled
@@ -29,12 +29,13 @@ rpm --import /tmp/Devel_YaST_Agama_Head_key.gpg
rm /tmp/Devel_YaST_Agama_Head_key.gpg
# import the openSUSE keys, but check if there is any
if stat -t /usr/lib/rpm/gnupg/keys/*.asc 2>/dev/null 1>/dev/null; then
- rpm --import /usr/lib/rpm/gnupg/keys/*.asc
+ rpm --import /usr/lib/rpm/gnupg/keys/*.asc
fi
# activate services
systemctl enable sshd.service
systemctl enable NetworkManager.service
+systemctl enable agama-cmdline-env.service
systemctl enable avahi-daemon.service
systemctl enable agama.service
systemctl enable agama-web-server.service
@@ -82,11 +83,11 @@ echo "root_disk=live:LABEL=$label" >>/etc/cmdline.d/10-liveroot.conf
# if there's a default network location, add it here
# echo "root_net=" >> /etc/cmdline.d/10-liveroot.conf
echo 'install_items+=" /etc/cmdline.d/10-liveroot.conf "' >/etc/dracut.conf.d/10-liveroot-file.conf
-echo 'add_dracutmodules+=" dracut-menu "' >>/etc/dracut.conf.d/10-liveroot-file.conf
+echo 'add_dracutmodules+=" dracut-menu agama-cmdline "' >>/etc/dracut.conf.d/10-liveroot-file.conf
-if [ "${arch}" = "s390x" ];then
- # workaround for custom bootloader setting
- touch /config.bootoptions
+if [ "${arch}" = "s390x" ]; then
+ # workaround for custom bootloader setting
+ touch /config.bootoptions
fi
# replace the @@LIVE_MEDIUM_LABEL@@ with the real Live partition label name from KIWI
@@ -111,7 +112,7 @@ rm /var/log/zypper.log /var/log/zypp/history
# reduce the "vim-data" content, this package is huge (37MB unpacked!), keep only
# support for JSON (for "agama config edit") and Ruby (fixing/debugging the Ruby
# service)
-rpm -ql vim-data | grep -v -e '/ruby.vim$' -e '/json.vim$' -e colors | xargs rm 2> /dev/null || true
+rpm -ql vim-data | grep -v -e '/ruby.vim$' -e '/json.vim$' -e colors | xargs rm 2>/dev/null || true
du -h -s /usr/{share,lib}/locale/
@@ -125,7 +126,7 @@ du -h -s /usr/{share,lib}/locale/
mkdir -p /etc/agama.d
# emulate "localectl list-locales" call, it cannot be used here because it
# insists on running systemd as PID 1 :-/
-ls -1 -d /usr/lib/locale/*.utf8 | sed -e "s#/usr/lib/locale/##" -e "s#utf8#UTF-8#" > /etc/agama.d/locales
+ls -1 -d /usr/lib/locale/*.utf8 | sed -e "s#/usr/lib/locale/##" -e "s#utf8#UTF-8#" >/etc/agama.d/locales
# delete translations and unusupported languages (makes ISO about 22MiB smaller)
# build list of ignore options for "ls" with supported languages like "-I cs* -I de* -I es* ..."
@@ -138,7 +139,7 @@ ls -1 "${IGNORE_OPTS[@]}" -I "en_US*" -I "C.*" /usr/lib/locale/ | xargs -I% sh -
# delete unused translations (MO files)
for t in zypper gettext-runtime p11-kit; do
- rm -f /usr/share/locale/*/LC_MESSAGES/$t.mo
+ rm -f /usr/share/locale/*/LC_MESSAGES/$t.mo
done
du -h -s /usr/{share,lib}/locale/
From 7e4e6dbfe4c5caea297bcbbce76e41641c4b51de Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Wed, 8 Jan 2025 09:58:37 +0100
Subject: [PATCH 082/147] fix id to match scc
---
products.d/sles_sap_160.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/products.d/sles_sap_160.yaml b/products.d/sles_sap_160.yaml
index 05088d31c1..39a4b00eb9 100644
--- a/products.d/sles_sap_160.yaml
+++ b/products.d/sles_sap_160.yaml
@@ -1,4 +1,4 @@
-id: SLES_SAP
+id: SLES-SAP
name: SUSE Linux Enterprise Server for SAP Applications 16.0 Beta
registration: "mandatory"
version: "16-0"
From b8a69c5ed05197642322b15bebf80730ab8e9eb4 Mon Sep 17 00:00:00 2001
From: Knut Anderssen
Date: Wed, 8 Jan 2025 09:23:57 +0000
Subject: [PATCH 083/147] Use a EnvironmentFile for agama
---
.../systemd/system/agama-cmdline-env.service | 17 -------------
live/root/usr/bin/agama-cmdline-env | 24 -------------------
live/src/config.sh | 1 -
rust/share/agama-web-server.service | 1 +
service/share/agama.service | 1 +
5 files changed, 2 insertions(+), 42 deletions(-)
delete mode 100644 live/root/etc/systemd/system/agama-cmdline-env.service
delete mode 100755 live/root/usr/bin/agama-cmdline-env
diff --git a/live/root/etc/systemd/system/agama-cmdline-env.service b/live/root/etc/systemd/system/agama-cmdline-env.service
deleted file mode 100644
index 096d5b5df4..0000000000
--- a/live/root/etc/systemd/system/agama-cmdline-env.service
+++ /dev/null
@@ -1,17 +0,0 @@
-[Unit]
-Description=Export the configuration from kernel command line as environment variables
-
-# before starting the Agama server
-Before=agama-web-server.service
-Before=agama.service
-
-# before the interactive setting methods so they can override it
-Before=live-password-dialog.service
-Before=live-password-systemd.service
-
-[Service]
-ExecStart=source /usr/bin/agama-cmdline-env
-Type=oneshot
-
-[Install]
-WantedBy=default.target
diff --git a/live/root/usr/bin/agama-cmdline-env b/live/root/usr/bin/agama-cmdline-env
deleted file mode 100755
index 4e4dded3b9..0000000000
--- a/live/root/usr/bin/agama-cmdline-env
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-
-#----[ import_agama_env ]----#
-function import_agama_env () {
-#--------------------------------------------------
-# import agama cmdline information as environment
-# variables to the current environment
-# ---
- TERM_SAVE=$TERM
- if [ -f /etc/agama.d/cmdline.conf ]; then
- IFS_SAVE=$IFS
-IFS="
-"
- for i in `cat /etc/agama.d/cmdline.conf | sed -e s'@=@%@'`;do
- varname=`echo $i | cut -f 1 -d% | tr -d " " | sed -e s'@\.@_@'`
- varvals=`echo $i | cut -f 2 -d%`
- varvals=`echo $varvals | sed -e s'@^ *@@' -e s'@ *$@@'`
- export $varname=$varvals
- done
- IFS=$IFS_SAVE
- fi
-}
-
-import_agama_env
diff --git a/live/src/config.sh b/live/src/config.sh
index 9a991eb07e..65f6e9f0c7 100644
--- a/live/src/config.sh
+++ b/live/src/config.sh
@@ -35,7 +35,6 @@ fi
# activate services
systemctl enable sshd.service
systemctl enable NetworkManager.service
-systemctl enable agama-cmdline-env.service
systemctl enable avahi-daemon.service
systemctl enable agama.service
systemctl enable agama-web-server.service
diff --git a/rust/share/agama-web-server.service b/rust/share/agama-web-server.service
index 0c8b27a4d8..210161d554 100644
--- a/rust/share/agama-web-server.service
+++ b/rust/share/agama-web-server.service
@@ -6,6 +6,7 @@ After=network-online.target agama.service agama-hostname.service
BindsTo=agama.service
[Service]
+EnvironmentFile=-"/etc/agama.d/cmdline.conf"
Environment="AGAMA_LOG=debug,zbus=info"
Type=notify
ExecStart=/usr/bin/agama-web-server serve --address :::80 --address2 :::443
diff --git a/service/share/agama.service b/service/share/agama.service
index f9b9bcfeb3..0392ff5783 100644
--- a/service/share/agama.service
+++ b/service/share/agama.service
@@ -5,6 +5,7 @@ After=network-online.target
[Service]
Type=forking
ExecStart=/usr/bin/agamactl --daemon
+EnviromentFile=-/etc/agama.d/cmdline.conf
PIDFile=/run/agama/bus.pid
User=root
TimeoutStopSec=5
From 90a2d77819f41fb79d03d85c09c91dd82f338db4 Mon Sep 17 00:00:00 2001
From: Knut Anderssen
Date: Wed, 8 Jan 2025 12:12:54 +0000
Subject: [PATCH 084/147] Added changelog
---
live/src/agama-installer.changes | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/live/src/agama-installer.changes b/live/src/agama-installer.changes
index 8d573634a3..7113bb56f0 100644
--- a/live/src/agama-installer.changes
+++ b/live/src/agama-installer.changes
@@ -1,3 +1,10 @@
+-------------------------------------------------------------------
+Wed Jan 8 12:10:39 UTC 2025 - Knut Anderssen
+
+- Make agama kernel cmdline options available in the sysroot at
+ /etc/agama.d/cmdline.conf and set it as a EnvironmentFile
+ in Agama related services (gh#agama-project/agama#1866).
+
-------------------------------------------------------------------
Tue Dec 10 12:46:06 UTC 2024 - Michal Filka
From 9a988714f05312064bcfdb18d82e69427b0662b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 8 Jan 2025 11:53:26 +0000
Subject: [PATCH 085/147] fix(rust): handle registration errors properly
---
rust/agama-lib/src/product/http_client.rs | 21 +++++++++++++++++++--
rust/agama-lib/src/product/store.rs | 13 ++-----------
rust/agama-lib/src/software/model.rs | 8 ++++++++
rust/agama-server/src/software/web.rs | 23 +++++++++--------------
4 files changed, 38 insertions(+), 27 deletions(-)
diff --git a/rust/agama-lib/src/product/http_client.rs b/rust/agama-lib/src/product/http_client.rs
index 424a8b49f9..29a36f3b3a 100644
--- a/rust/agama-lib/src/product/http_client.rs
+++ b/rust/agama-lib/src/product/http_client.rs
@@ -18,6 +18,7 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.
+use crate::software::model::RegistrationError;
use crate::software::model::RegistrationInfo;
use crate::software::model::RegistrationParams;
use crate::software::model::SoftwareConfig;
@@ -64,13 +65,29 @@ impl ProductHTTPClient {
}
/// register product
- pub async fn register(&self, key: &str, email: &str) -> Result<(u32, String), ServiceError> {
+ pub async fn register(&self, key: &str, email: &str) -> Result<(), ServiceError> {
// note RegistrationParams != RegistrationInfo, fun!
let params = RegistrationParams {
key: key.to_owned(),
email: email.to_owned(),
};
+ let result = self
+ .client
+ .post_void("/software/registration", ¶ms)
+ .await;
- self.client.post("/software/registration", ¶ms).await
+ let Err(error) = result else {
+ return Ok(());
+ };
+
+ let message = match error {
+ ServiceError::BackendError(_, details) => {
+ let details: RegistrationError = serde_json::from_str(&details).unwrap();
+ format!("{} (error code: {})", details.message, details.id)
+ }
+ _ => format!("Could not register the product: #{error:?}"),
+ };
+
+ Err(ServiceError::FailedRegistration(message))
}
}
diff --git a/rust/agama-lib/src/product/store.rs b/rust/agama-lib/src/product/store.rs
index d43a6166c4..c1a3f42297 100644
--- a/rust/agama-lib/src/product/store.rs
+++ b/rust/agama-lib/src/product/store.rs
@@ -68,17 +68,8 @@ impl ProductStore {
}
}
if let Some(reg_code) = &settings.registration_code {
- let (result, message);
- if let Some(email) = &settings.registration_email {
- (result, message) = self.product_client.register(reg_code, email).await?;
- } else {
- (result, message) = self.product_client.register(reg_code, "").await?;
- }
- // FIXME: name the magic numbers. 3 is Registration not required
- // FIXME: well don't register when not required (no regcode in profile)
- if result != 0 && result != 3 {
- return Err(ServiceError::FailedRegistration(message));
- }
+ let email = settings.registration_email.as_deref().unwrap_or("");
+ self.product_client.register(reg_code, email).await?;
probe = true;
}
diff --git a/rust/agama-lib/src/software/model.rs b/rust/agama-lib/src/software/model.rs
index 32618542f1..8556dae11d 100644
--- a/rust/agama-lib/src/software/model.rs
+++ b/rust/agama-lib/src/software/model.rs
@@ -71,6 +71,14 @@ pub enum RegistrationRequirement {
Mandatory = 2,
}
+#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
+pub struct RegistrationError {
+ /// ID of error. See dbus API for possible values
+ pub id: u32,
+ /// human readable error string intended to be displayed to user
+ pub message: String,
+}
+
/// Software resolvable type (package or pattern).
#[derive(Deserialize, Serialize, strum::Display, utoipa::ToSchema)]
#[strum(serialize_all = "camelCase")]
diff --git a/rust/agama-server/src/software/web.rs b/rust/agama-server/src/software/web.rs
index f800c75cab..40d49be018 100644
--- a/rust/agama-server/src/software/web.rs
+++ b/rust/agama-server/src/software/web.rs
@@ -37,7 +37,10 @@ use agama_lib::{
error::ServiceError,
product::{proxies::RegistrationProxy, Product, ProductClient},
software::{
- model::{RegistrationInfo, RegistrationParams, ResolvableParams, SoftwareConfig},
+ model::{
+ RegistrationError, RegistrationInfo, RegistrationParams, ResolvableParams,
+ SoftwareConfig,
+ },
proxies::{Software1Proxy, SoftwareProductProxy},
Pattern, SelectedBy, SoftwareClient, UnknownSelectedBy,
},
@@ -49,7 +52,7 @@ use axum::{
routing::{get, post, put},
Json, Router,
};
-use serde::{Deserialize, Serialize};
+use serde::Serialize;
use std::collections::HashMap;
use tokio_stream::{Stream, StreamExt};
@@ -248,14 +251,6 @@ async fn get_registration(
Ok(Json(result))
}
-#[derive(Clone, Serialize, Deserialize, utoipa::ToSchema)]
-pub struct FailureDetails {
- /// ID of error. See dbus API for possible values
- id: u32,
- /// human readable error string intended to be displayed to user
- message: String,
-}
-
/// Register product
///
/// * `state`: service state.
@@ -265,7 +260,7 @@ pub struct FailureDetails {
context_path = "/api/software",
responses(
(status = 204, description = "registration successfull"),
- (status = 422, description = "Registration failed. Details are in body", body = FailureDetails),
+ (status = 422, description = "Registration failed. Details are in body", body = RegistrationError),
(status = 400, description = "The D-Bus service could not perform the action")
)
)]
@@ -274,10 +269,10 @@ async fn register(
Json(config): Json,
) -> Result {
let (id, message) = state.product.register(&config.key, &config.email).await?;
- let details = FailureDetails { id, message };
if id == 0 {
Ok((StatusCode::NO_CONTENT, ().into_response()))
} else {
+ let details = RegistrationError { id, message };
Ok((
StatusCode::UNPROCESSABLE_ENTITY,
Json(details).into_response(),
@@ -294,13 +289,13 @@ async fn register(
context_path = "/api/software",
responses(
(status = 200, description = "deregistration successfull"),
- (status = 422, description = "De-registration failed. Details are in body", body = FailureDetails),
+ (status = 422, description = "De-registration failed. Details are in body", body = RegistrationError),
(status = 400, description = "The D-Bus service could not perform the action")
)
)]
async fn deregister(State(state): State>) -> Result {
let (id, message) = state.product.deregister().await?;
- let details = FailureDetails { id, message };
+ let details = RegistrationError { id, message };
if id == 0 {
Ok((StatusCode::NO_CONTENT, ().into_response()))
} else {
From 9807c91405043618e7b12d2cd26229bf0e9dec68 Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Wed, 8 Jan 2025 14:00:18 +0100
Subject: [PATCH 086/147] allow sles4SAP only on supported architectures
---
products.d/sles_sap_160.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/products.d/sles_sap_160.yaml b/products.d/sles_sap_160.yaml
index 39a4b00eb9..30c86b7671 100644
--- a/products.d/sles_sap_160.yaml
+++ b/products.d/sles_sap_160.yaml
@@ -1,5 +1,6 @@
id: SLES-SAP
name: SUSE Linux Enterprise Server for SAP Applications 16.0 Beta
+archs: x86_64,aarch64,ppc
registration: "mandatory"
version: "16-0"
# ------------------------------------------------------------------------------
From f1de0ade3383d1a3fc79f7e77faa689737e7139d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?=
Date: Wed, 8 Jan 2025 13:04:46 +0000
Subject: [PATCH 087/147] Update
live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
---
.../lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh | 1 -
1 file changed, 1 deletion(-)
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
index f006ca30f1..eca0743012 100755
--- a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
@@ -16,7 +16,6 @@ get_agama_args() {
if [ -n "$_found" ]; then
printf "Agama variable found ($_i)"
- printf $_i
if ! strstr "$_i" "="; then
# Set the variable as a boolean if there is no assignation
_i="${_i}=1"
From c75fb72fbc03a7452fabf30bc9d88e3517eb1b58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Wed, 8 Jan 2025 13:06:01 +0000
Subject: [PATCH 088/147] fix(web): restore cancel behaviour at product
selection page
By displaying the action only when there is a product already
selected. It reverts a mistake introduced at 2a611955943.
---
.../product/ProductSelectionPage.test.tsx | 66 +++++++++++++------
.../product/ProductSelectionPage.tsx | 2 +-
2 files changed, 47 insertions(+), 21 deletions(-)
diff --git a/web/src/components/product/ProductSelectionPage.test.tsx b/web/src/components/product/ProductSelectionPage.test.tsx
index 4209e2da58..a1682bc38d 100644
--- a/web/src/components/product/ProductSelectionPage.test.tsx
+++ b/web/src/components/product/ProductSelectionPage.test.tsx
@@ -27,7 +27,6 @@ import { ProductSelectionPage } from "~/components/product";
import { Product } from "~/types/software";
import { useProduct } from "~/queries/software";
-const mockConfigMutation = jest.fn();
const tumbleweed: Product = {
id: "Tumbleweed",
name: "openSUSE Tumbleweed",
@@ -42,37 +41,64 @@ const microOs: Product = {
description: "MicroOS description",
};
+const mockConfigMutation = jest.fn();
+let mockSelectedProduct: Product;
+
jest.mock("~/queries/software", () => ({
...jest.requireActual("~/queries/software"),
useProduct: (): ReturnType => {
return {
products: [tumbleweed, microOs],
- selectedProduct: tumbleweed,
+ selectedProduct: mockSelectedProduct,
};
},
useProductChanges: () => jest.fn(),
useConfigMutation: () => ({ mutate: mockConfigMutation }),
}));
-describe("when the user chooses a product and hits the confirmation button", () => {
- it("triggers the product selection", async () => {
- const { user } = installerRender();
- const productOption = screen.getByRole("radio", { name: microOs.name });
- const selectButton = screen.getByRole("button", { name: "Select" });
- await user.click(productOption);
- await user.click(selectButton);
- expect(mockConfigMutation).toHaveBeenCalledWith({ product: microOs.id });
+describe("ProductSelectionPage", () => {
+ beforeEach(() => {
+ mockSelectedProduct = tumbleweed;
+ });
+
+ describe("when there is a product already selected", () => {
+ it("renders the Cancel button", () => {
+ installerRender();
+ screen.getByRole("button", { name: "Cancel" });
+ });
+ });
+
+ describe("when there is not a product selected yet", () => {
+ beforeEach(() => {
+ mockSelectedProduct = undefined;
+ });
+
+ it("does not render the Cancel button", () => {
+ installerRender();
+ expect(screen.queryByRole("button", { name: "Cancel" })).toBeNull();
+ });
+ });
+
+ describe("when the user chooses a product and hits the confirmation button", () => {
+ it("triggers the product selection", async () => {
+ const { user } = installerRender();
+ const productOption = screen.getByRole("radio", { name: microOs.name });
+ const selectButton = screen.getByRole("button", { name: "Select" });
+ await user.click(productOption);
+ await user.click(selectButton);
+ expect(mockConfigMutation).toHaveBeenCalledWith({ product: microOs.id });
+ });
});
-});
-describe("when the user chooses a product but hits the cancel button", () => {
- it("does not trigger the product selection and goes back", async () => {
- const { user } = installerRender();
- const productOption = screen.getByRole("radio", { name: microOs.name });
- const cancelButton = screen.getByRole("button", { name: "Cancel" });
- await user.click(productOption);
- await user.click(cancelButton);
- expect(mockConfigMutation).not.toHaveBeenCalled();
- expect(mockNavigateFn).toHaveBeenCalledWith("/");
+ describe("when the user chooses a product but hits the cancel button", () => {
+ it("does not trigger the product selection and goes back", async () => {
+ const { user } = installerRender();
+ const productOption = screen.getByRole("radio", { name: microOs.name });
+ const cancelButton = screen.getByRole("button", { name: "Cancel" });
+ await user.click(productOption);
+ await user.click(cancelButton);
+ expect(mockConfigMutation).not.toHaveBeenCalled();
+ expect(mockNavigateFn).toHaveBeenCalledWith("/");
+ });
});
});
diff --git a/web/src/components/product/ProductSelectionPage.tsx b/web/src/components/product/ProductSelectionPage.tsx
index c24135c712..aa5d918f64 100644
--- a/web/src/components/product/ProductSelectionPage.tsx
+++ b/web/src/components/product/ProductSelectionPage.tsx
@@ -139,7 +139,7 @@ function ProductSelectionPage() {
-
+ {selectedProduct && !isLoading && }
Date: Wed, 8 Jan 2025 13:14:24 +0000
Subject: [PATCH 089/147] doc(web): update changes file
---
web/package/agama-web-ui.changes | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes
index 398cf686d1..f83863a04d 100644
--- a/web/package/agama-web-ui.changes
+++ b/web/package/agama-web-ui.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Wed Jan 8 13:12:44 UTC 2025 - David Diaz
+
+- Show the cancel action at product selection page only when
+ a product is already selected (gh#agama-project/agama#1881).
+
-------------------------------------------------------------------
Fri Dec 20 12:53:41 UTC 2024 - David Diaz
From cbc78f6e491492f72620204a235f8486129de09c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 8 Jan 2025 13:16:34 +0000
Subject: [PATCH 090/147] fix(live): use gems for the default Ruby version
---
live/src/agama-installer.kiwi | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/live/src/agama-installer.kiwi b/live/src/agama-installer.kiwi
index 8a1dab1e4c..4dac7176c9 100644
--- a/live/src/agama-installer.kiwi
+++ b/live/src/agama-installer.kiwi
@@ -144,6 +144,8 @@
+
+
@@ -166,8 +168,6 @@
-
-
@@ -178,8 +178,6 @@
-
-
@@ -189,8 +187,6 @@
-
-
From 3850472a58214787db8829f2fa4dd227518a38cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?=
Date: Wed, 8 Jan 2025 14:09:23 +0000
Subject: [PATCH 091/147] doc(rust): update changes files
---
products.d/agama-products.changes | 6 ++++++
rust/package/agama.changes | 6 ++++++
service/package/rubygem-agama-yast.changes | 6 ++++++
web/package/agama-web-ui.changes | 6 ++++++
4 files changed, 24 insertions(+)
diff --git a/products.d/agama-products.changes b/products.d/agama-products.changes
index 3c0380ef27..c30beb6d34 100644
--- a/products.d/agama-products.changes
+++ b/products.d/agama-products.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Wed Jan 8 14:07:21 UTC 2025 - Imobach Gonzalez Sosa
+
+- Add support for products registration (jsc#PED-11192,
+ gh#agama-project/agama#1809).
+
-------------------------------------------------------------------
Mon Jan 6 14:41:28 UTC 2025 - Angela Briel
diff --git a/rust/package/agama.changes b/rust/package/agama.changes
index ecf3c6384f..8fa24ecd3f 100644
--- a/rust/package/agama.changes
+++ b/rust/package/agama.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Wed Jan 8 14:05:34 UTC 2025 - Imobach Gonzalez Sosa
+
+- Add support for products registration (jsc#PED-11192,
+ gh#agama-project/agama#1809).
+
-------------------------------------------------------------------
Fri Dec 20 12:17:26 UTC 2024 - Josef Reidinger
diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes
index 217912e49d..f9f77e40ed 100644
--- a/service/package/rubygem-agama-yast.changes
+++ b/service/package/rubygem-agama-yast.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Wed Jan 8 14:05:53 UTC 2025 - Imobach Gonzalez Sosa
+
+- Add support for products registration (jsc#PED-11192,
+ gh#agama-project/agama#1809).
+
-------------------------------------------------------------------
Mon Dec 23 18:40:01 UTC 2024 - Josef Reidinger
diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes
index 398cf686d1..6e0d778d32 100644
--- a/web/package/agama-web-ui.changes
+++ b/web/package/agama-web-ui.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Wed Jan 8 14:07:13 UTC 2025 - Imobach Gonzalez Sosa
+
+- Add support for products registration (jsc#PED-11192,
+ gh#agama-project/agama#1809).
+
-------------------------------------------------------------------
Fri Dec 20 12:53:41 UTC 2024 - David Diaz
From 6bd88fc2ccc4c6ee50ca5470632ddf2d1eff8cab Mon Sep 17 00:00:00 2001
From: Knut Anderssen
Date: Wed, 8 Jan 2025 14:16:28 +0000
Subject: [PATCH 092/147] Fix indentation
---
.../99agama-cmdline/agama-cmdline-conf.sh | 36 +++++++++----------
.../modules.d/99agama-cmdline/module-setup.sh | 10 +++---
.../99agama-cmdline/save-agama-conf.sh | 6 ++--
live/src/config.sh | 12 +++----
4 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
index eca0743012..e1ec13f79b 100755
--- a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/agama-cmdline-conf.sh
@@ -5,27 +5,27 @@
. /lib/dracut-lib.sh
get_agama_args() {
- local _i _found
+ local _i _found
- for _i in $CMDLINE; do
- case $_i in
- LIBSTORAGE_* | YAST_* | agama* | Y2* | ZYPP_*)
- _found=1
- ;;
- esac
+ for _i in $CMDLINE; do
+ case $_i in
+ LIBSTORAGE_* | YAST_* | agama* | Y2* | ZYPP_*)
+ _found=1
+ ;;
+ esac
- if [ -n "$_found" ]; then
- printf "Agama variable found ($_i)"
- if ! strstr "$_i" "="; then
- # Set the variable as a boolean if there is no assignation
- _i="${_i}=1"
- fi
- echo $_i >>/etc/cmdline.d/99-agama-cmdline.conf
- fi
- unset _found
- done
+ if [ -n "$_found" ]; then
+ printf "Agama variable found ($_i)"
+ if ! strstr "$_i" "="; then
+ # Set the variable as a boolean if there is no assignation
+ _i="${_i}=1"
+ fi
+ echo $_i >>/etc/cmdline.d/99-agama-cmdline.conf
+ fi
+ unset _found
+ done
- return 0
+ return 0
}
get_agama_args
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh
index d957ddab0e..24a6bcd321 100755
--- a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/module-setup.sh
@@ -2,20 +2,20 @@
# called by dracut
check() {
- return 0
+ return 0
}
# called by dracut
depends() {
- return 0
+ return 0
}
installkernel() {
- return 0
+ return 0
}
# called by dracut
install() {
- inst_hook cmdline 99 "$moddir/agama-cmdline-conf.sh"
- inst_hook pre-pivot 99 "$moddir/save-agama-conf.sh"
+ inst_hook cmdline 99 "$moddir/agama-cmdline-conf.sh"
+ inst_hook pre-pivot 99 "$moddir/save-agama-conf.sh"
}
diff --git a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh
index 99a328b7ee..612843ddd7 100755
--- a/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh
+++ b/live/root/usr/lib/dracut/modules.d/99agama-cmdline/save-agama-conf.sh
@@ -5,7 +5,7 @@
. /lib/dracut-lib.sh
if [ -e /etc/cmdline.d/99-agama-cmdline.conf ]; then
- echo "Creating agama conf"
- mkdir -p "$NEWROOT/etc/agama.d"
- cp /etc/cmdline.d/99-agama-cmdline.conf "$NEWROOT/etc/agama.d/cmdline.conf"
+ echo "Creating agama conf"
+ mkdir -p "$NEWROOT/etc/agama.d"
+ cp /etc/cmdline.d/99-agama-cmdline.conf "$NEWROOT/etc/agama.d/cmdline.conf"
fi
diff --git a/live/src/config.sh b/live/src/config.sh
index 65f6e9f0c7..607fe25094 100644
--- a/live/src/config.sh
+++ b/live/src/config.sh
@@ -16,7 +16,7 @@ suseSetupProduct
DISTRO=$(grep "^NAME" /etc/os-release | cut -f2 -d\= | tr -d '"' | tr " " "_")
REPO="/etc/zypp/repos.d/agama-${DISTRO}.repo"
if [ -f "${REPO}.disabled" ]; then
- mv "${REPO}.disabled" $REPO
+ mv "${REPO}.disabled" $REPO
fi
rm /etc/zypp/repos.d/*.disabled
@@ -29,7 +29,7 @@ rpm --import /tmp/Devel_YaST_Agama_Head_key.gpg
rm /tmp/Devel_YaST_Agama_Head_key.gpg
# import the openSUSE keys, but check if there is any
if stat -t /usr/lib/rpm/gnupg/keys/*.asc 2>/dev/null 1>/dev/null; then
- rpm --import /usr/lib/rpm/gnupg/keys/*.asc
+ rpm --import /usr/lib/rpm/gnupg/keys/*.asc
fi
# activate services
@@ -85,8 +85,8 @@ echo 'install_items+=" /etc/cmdline.d/10-liveroot.conf "' >/etc/dracut.conf.d/10
echo 'add_dracutmodules+=" dracut-menu agama-cmdline "' >>/etc/dracut.conf.d/10-liveroot-file.conf
if [ "${arch}" = "s390x" ]; then
- # workaround for custom bootloader setting
- touch /config.bootoptions
+ # workaround for custom bootloader setting
+ touch /config.bootoptions
fi
# replace the @@LIVE_MEDIUM_LABEL@@ with the real Live partition label name from KIWI
@@ -138,7 +138,7 @@ ls -1 "${IGNORE_OPTS[@]}" -I "en_US*" -I "C.*" /usr/lib/locale/ | xargs -I% sh -
# delete unused translations (MO files)
for t in zypper gettext-runtime p11-kit; do
- rm -f /usr/share/locale/*/LC_MESSAGES/$t.mo
+ rm -f /usr/share/locale/*/LC_MESSAGES/$t.mo
done
du -h -s /usr/{share,lib}/locale/
@@ -180,7 +180,7 @@ du -h -s /lib/modules /lib/firmware
# disable the services included by dependencies
for s in purge-kernels; do
- systemctl -f disable $s || true
+ systemctl -f disable $s || true
done
# Only used for OpenCL and X11 acceleration on vmwgfx (?), saves ~50MiB
From 0cedb6a9491547478cd1acefd1d4005a25608f64 Mon Sep 17 00:00:00 2001
From: Josef Reidinger
Date: Wed, 8 Jan 2025 16:01:41 +0100
Subject: [PATCH 093/147] fix product registration finish
---
service/lib/agama/registration.rb | 4 ++-
service/test/agama/registration_test.rb | 36 +++++++++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/service/lib/agama/registration.rb b/service/lib/agama/registration.rb
index 655dc96c1d..658a1c9c83 100644
--- a/service/lib/agama/registration.rb
+++ b/service/lib/agama/registration.rb
@@ -142,7 +142,9 @@ def deregister
def finish
return unless reg_code
- files = [credentials_path(@credentials_file), SUSE::Connect::YaST::GLOBAL_CREDENTIALS_PATH]
+ files = [GLOBAL_CREDENTIALS_PATH]
+ files << credentials_path(@credentials_file) if @credentials_file
+
files.each do |file|
dest = File.join(Yast::Installation.destdir, file)
FileUtils.cp(file, dest)
diff --git a/service/test/agama/registration_test.rb b/service/test/agama/registration_test.rb
index c62e258a61..748b35df89 100644
--- a/service/test/agama/registration_test.rb
+++ b/service/test/agama/registration_test.rb
@@ -33,6 +33,7 @@
subject { described_class.new(manager, logger) }
let(:manager) { instance_double(Agama::Software::Manager) }
+ let(:product) { Agama::Software::Product.new("test").tap { |p| p.version = "5.0" } }
let(:logger) { Logger.new($stdout, level: :warn) }
@@ -375,4 +376,39 @@
end
end
end
+
+ describe "#finish" do
+ context "system is not registered" do
+ before do
+ subject.instance_variable_set(:@reg_code, nil)
+ end
+
+ it "do nothing" do
+ expect(::FileUtils).to_not receive(:cp)
+
+ subject.finish
+ end
+ end
+
+ context "system is registered" do
+ before do
+ subject.instance_variable_set(:@reg_code, "test")
+ subject.instance_variable_set(:@credentials_file, "test")
+ Yast::Installation.destdir = "/mnt"
+ allow(::FileUtils).to receive(:cp)
+ end
+
+ it "copies global credentials file" do
+ expect(::FileUtils).to receive(:cp).with("/etc/zypp/credentials.d/SCCcredentials", "/mnt/etc/zypp/credentials.d/SCCcredentials")
+
+ subject.finish
+ end
+
+ it "copies product credentials file" do
+ expect(::FileUtils).to receive(:cp).with("/etc/zypp/credentials.d/test", "/mnt/etc/zypp/credentials.d/test")
+
+ subject.finish
+ end
+ end
+ end
end
From 395bbe437dc9c480ce094892bd035eb290ad2217 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20D=C3=ADaz=20Gonz=C3=A1lez?=
Date: Wed, 8 Jan 2025 15:05:36 +0000
Subject: [PATCH 094/147] fix(web): use product ID instead of generated slug
The product selection page was using a dynamically generated slug based
on the product name. Since each product already has a unique ID, it is
more reliable and efficient to use it instead of generating a new one.
---
web/src/components/product/ProductSelectionPage.tsx | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/web/src/components/product/ProductSelectionPage.tsx b/web/src/components/product/ProductSelectionPage.tsx
index aa5d918f64..a4ac2009dd 100644
--- a/web/src/components/product/ProductSelectionPage.tsx
+++ b/web/src/components/product/ProductSelectionPage.tsx
@@ -39,7 +39,6 @@ import { Center } from "~/components/layout";
import { useConfigMutation, useProduct } from "~/queries/software";
import pfTextStyles from "@patternfly/react-styles/css/utilities/Text/text";
import pfRadioStyles from "@patternfly/react-styles/css/components/Radio/radio";
-import { slugify } from "~/utils";
import { sprintf } from "sprintf-js";
import { _ } from "~/i18n";
import { useNavigate } from "react-router-dom";
@@ -51,8 +50,7 @@ const ResponsiveGridItem = ({ children }) => (
);
const Option = ({ product, isChecked, onChange }) => {
- const id = slugify(product.name);
- const detailsId = `${id}-details`;
+ const detailsId = `${product.id}-details`;
const logoSrc = `assets/logos/${product.icon}`;
// TRANSLATORS: %s will be replaced by a product name. E.g., "openSUSE Tumbleweed"
const logoAltText = sprintf(_("%s logo"), product.name);
@@ -63,7 +61,7 @@ const Option = ({ product, isChecked, onChange }) => {
{