feat: make version optional in registry package remove, add --force flag

When version is omitted, removes the entire package entry. Requires
--force if the package has any existing versions.
This commit is contained in:
Aiden McClelland
2026-04-01 17:06:51 -06:00
parent de7fbeff2c
commit 22b2ee01d7
5 changed files with 93 additions and 31 deletions

View File

@@ -1843,11 +1843,11 @@ registry.os.promote.version-not-found:
# registry/package/mod.rs # registry/package/mod.rs
registry.package.remove-not-exist: registry.package.remove-not-exist:
en_US: "%{id}@%{version}%{sighash} does not exist, so not removed" en_US: "%{id}%{version}%{sighash} does not exist, so not removed"
de_DE: "%{id}@%{version}%{sighash} existiert nicht, daher nicht entfernt" de_DE: "%{id}%{version}%{sighash} existiert nicht, daher nicht entfernt"
es_ES: "%{id}@%{version}%{sighash} no existe, por lo que no se eliminó" es_ES: "%{id}%{version}%{sighash} no existe, por lo que no se eliminó"
fr_FR: "%{id}@%{version}%{sighash} n'existe pas, donc non supprimé" fr_FR: "%{id}%{version}%{sighash} n'existe pas, donc non supprimé"
pl_PL: "%{id}@%{version}%{sighash} nie istnieje, więc nie usunięto" pl_PL: "%{id}%{version}%{sighash} nie istnieje, więc nie usunięto"
# registry/package/add.rs # registry/package/add.rs
registry.package.add.must-specify-url: registry.package.add.must-specify-url:
@@ -1871,6 +1871,13 @@ registry.package.missing-signer:
fr_FR: "Signataire manquant" fr_FR: "Signataire manquant"
pl_PL: "Brak sygnatariusza" pl_PL: "Brak sygnatariusza"
registry.package.remove-has-versions:
en_US: "Package %{id} has versions; use --force to remove"
de_DE: "Paket %{id} hat Versionen; verwenden Sie --force zum Entfernen"
es_ES: "El paquete %{id} tiene versiones; use --force para eliminar"
fr_FR: "Le paquet %{id} a des versions ; utilisez --force pour supprimer"
pl_PL: "Pakiet %{id} ma wersje; użyj --force aby usunąć"
registry.package.unauthorized: registry.package.unauthorized:
en_US: "Unauthorized" en_US: "Unauthorized"
de_DE: "Nicht autorisiert" de_DE: "Nicht autorisiert"
@@ -2910,6 +2917,13 @@ help.arg.force-clear-task:
fr_FR: "Forcer la suppression de la tâche même si elle est en cours" fr_FR: "Forcer la suppression de la tâche même si elle est en cours"
pl_PL: "Wymuś wyczyszczenie zadania nawet jeśli jest uruchomione" pl_PL: "Wymuś wyczyszczenie zadania nawet jeśli jest uruchomione"
help.arg.force-remove-package:
en_US: "Force removal even if the package has versions"
de_DE: "Entfernung erzwingen, auch wenn das Paket Versionen hat"
es_ES: "Forzar la eliminación aunque el paquete tenga versiones"
fr_FR: "Forcer la suppression même si le paquet a des versions"
pl_PL: "Wymuś usunięcie nawet jeśli pakiet ma wersje"
help.arg.force-stderr-tty: help.arg.force-stderr-tty:
en_US: "Force stderr to be treated as a TTY" en_US: "Force stderr to be treated as a TTY"
de_DE: "stderr als TTY behandeln erzwingen" de_DE: "stderr als TTY behandeln erzwingen"

View File

@@ -4,7 +4,7 @@
.SH NAME .SH NAME
start\-cli\-registry\-package\-remove \- Remove package from registry start\-cli\-registry\-package\-remove \- Remove package from registry
.SH SYNOPSIS .SH SYNOPSIS
\fBstart\-cli registry package remove\fR [\fB\-\-sighash\fR] [\fB\-h\fR|\fB\-\-help\fR] <\fIID\fR> <\fIVERSION\fR> \fBstart\-cli registry package remove\fR [\fB\-\-sighash\fR] [\fB\-\-force\fR] [\fB\-h\fR|\fB\-\-help\fR] <\fIID\fR> [\fIVERSION\fR]
.SH DESCRIPTION .SH DESCRIPTION
Remove package from registry Remove package from registry
.SH OPTIONS .SH OPTIONS
@@ -12,11 +12,14 @@ Remove package from registry
\fB\-\-sighash\fR \fI<SIGHASH>\fR \fB\-\-sighash\fR \fI<SIGHASH>\fR
Hash for signature verification Hash for signature verification
.TP .TP
\fB\-\-force\fR
Force removal even if the package has versions
.TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
Print help Print help
.TP .TP
<\fIID\fR> <\fIID\fR>
Package identifier Package identifier
.TP .TP
<\fIVERSION\fR> [\fIVERSION\fR]
Package version Package version

View File

@@ -4,7 +4,7 @@
.SH NAME .SH NAME
start\-registry\-package\-remove \- Remove package from registry start\-registry\-package\-remove \- Remove package from registry
.SH SYNOPSIS .SH SYNOPSIS
\fBstart\-registry package remove\fR [\fB\-\-sighash\fR] [\fB\-h\fR|\fB\-\-help\fR] <\fIID\fR> <\fIVERSION\fR> \fBstart\-registry package remove\fR [\fB\-\-sighash\fR] [\fB\-\-force\fR] [\fB\-h\fR|\fB\-\-help\fR] <\fIID\fR> [\fIVERSION\fR]
.SH DESCRIPTION .SH DESCRIPTION
Remove package from registry Remove package from registry
.SH OPTIONS .SH OPTIONS
@@ -12,11 +12,14 @@ Remove package from registry
\fB\-\-sighash\fR \fI<SIGHASH>\fR \fB\-\-sighash\fR \fI<SIGHASH>\fR
Hash for signature verification Hash for signature verification
.TP .TP
\fB\-\-force\fR
Force removal even if the package has versions
.TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
Print help Print help
.TP .TP
<\fIID\fR> <\fIID\fR>
Package identifier Package identifier
.TP .TP
<\fIVERSION\fR> [\fIVERSION\fR]
Package version Package version

View File

@@ -226,9 +226,12 @@ pub struct RemovePackageParams {
#[arg(help = "help.arg.package-id")] #[arg(help = "help.arg.package-id")]
pub id: PackageId, pub id: PackageId,
#[arg(help = "help.arg.package-version")] #[arg(help = "help.arg.package-version")]
pub version: VersionString, pub version: Option<VersionString>,
#[arg(long, help = "help.arg.signature-hash")] #[arg(long, help = "help.arg.signature-hash")]
pub sighash: Option<Base64<[u8; 32]>>, pub sighash: Option<Base64<[u8; 32]>>,
#[arg(long, help = "help.arg.force-remove-package")]
#[serde(default)]
pub force: bool,
#[ts(skip)] #[ts(skip)]
#[arg(skip)] #[arg(skip)]
#[serde(rename = "__Auth_signer")] #[serde(rename = "__Auth_signer")]
@@ -241,6 +244,7 @@ pub async fn remove_package(
id, id,
version, version,
sighash, sighash,
force,
signer, signer,
}: RemovePackageParams, }: RemovePackageParams,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
@@ -256,7 +260,8 @@ pub async fn remove_package(
let rev = ctx let rev = ctx
.db .db
.mutate(|db| { .mutate(|db| {
if db.as_admins().de()?.contains(&signer_guid) let is_admin = db.as_admins().de()?.contains(&signer_guid);
let is_authorized = is_admin
|| db || db
.as_index() .as_index()
.as_package() .as_package()
@@ -266,28 +271,61 @@ pub async fn remove_package(
.as_authorized() .as_authorized()
.de()? .de()?
.get(&signer_guid) .get(&signer_guid)
.map_or(false, |v| version.satisfies(v)) .map_or(false, |v| {
{ version
if let Some(package) = db .as_ref()
.as_index_mut() .map_or(true, |version| version.satisfies(v))
.as_package_mut() });
.as_packages_mut() if is_authorized {
.as_idx_mut(&id) if let Some(version) = &version {
{ if let Some(package) = db
if let Some(sighash) = sighash { .as_index_mut()
if if let Some(package) = package.as_versions_mut().as_idx_mut(&version) { .as_package_mut()
package.as_s9pks_mut().mutate(|s| { .as_packages_mut()
s.retain(|(_, asset)| asset.commitment.root_sighash != sighash); .as_idx_mut(&id)
Ok(s.is_empty()) {
})? if let Some(sighash) = sighash {
if if let Some(package) =
package.as_versions_mut().as_idx_mut(version)
{
package.as_s9pks_mut().mutate(|s| {
s.retain(|(_, asset)| {
asset.commitment.root_sighash != sighash
});
Ok(s.is_empty())
})?
} else {
false
} {
package.as_versions_mut().remove(version)?;
}
} else { } else {
false package.as_versions_mut().remove(version)?;
} {
package.as_versions_mut().remove(&version)?;
} }
} else {
package.as_versions_mut().remove(&version)?;
} }
} else {
let has_versions = db
.as_index()
.as_package()
.as_packages()
.as_idx(&id)
.map(|p| {
p.as_versions()
.as_entries()
.map(|e| !e.is_empty())
.unwrap_or(false)
})
.unwrap_or(false);
if has_versions && !force {
return Err(Error::new(
eyre!("{}", t!("registry.package.remove-has-versions", id = id)),
ErrorKind::InvalidRequest,
));
}
db.as_index_mut()
.as_package_mut()
.as_packages_mut()
.remove(&id)?;
} }
Ok(()) Ok(())
} else { } else {

View File

@@ -56,7 +56,11 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
t!( t!(
"registry.package.remove-not-exist", "registry.package.remove-not-exist",
id = args.params.id, id = args.params.id,
version = args.params.version, version = args
.params
.version
.as_ref()
.map_or(String::new(), |v| format!("@{v}")),
sighash = args sighash = args
.params .params
.sighash .sighash