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.remove-not-exist:
en_US: "%{id}@%{version}%{sighash} does not exist, so not removed"
de_DE: "%{id}@%{version}%{sighash} existiert nicht, daher nicht entfernt"
es_ES: "%{id}@%{version}%{sighash} no existe, por lo que no se eliminó"
fr_FR: "%{id}@%{version}%{sighash} n'existe pas, donc non supprimé"
pl_PL: "%{id}@%{version}%{sighash} nie istnieje, więc nie usunięto"
en_US: "%{id}%{version}%{sighash} does not exist, so not removed"
de_DE: "%{id}%{version}%{sighash} existiert nicht, daher nicht entfernt"
es_ES: "%{id}%{version}%{sighash} no existe, por lo que no se eliminó"
fr_FR: "%{id}%{version}%{sighash} n'existe pas, donc non supprimé"
pl_PL: "%{id}%{version}%{sighash} nie istnieje, więc nie usunięto"
# registry/package/add.rs
registry.package.add.must-specify-url:
@@ -1871,6 +1871,13 @@ registry.package.missing-signer:
fr_FR: "Signataire manquant"
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:
en_US: "Unauthorized"
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"
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:
en_US: "Force stderr to be treated as a TTY"
de_DE: "stderr als TTY behandeln erzwingen"

View File

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

View File

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

View File

@@ -226,9 +226,12 @@ pub struct RemovePackageParams {
#[arg(help = "help.arg.package-id")]
pub id: PackageId,
#[arg(help = "help.arg.package-version")]
pub version: VersionString,
pub version: Option<VersionString>,
#[arg(long, help = "help.arg.signature-hash")]
pub sighash: Option<Base64<[u8; 32]>>,
#[arg(long, help = "help.arg.force-remove-package")]
#[serde(default)]
pub force: bool,
#[ts(skip)]
#[arg(skip)]
#[serde(rename = "__Auth_signer")]
@@ -241,6 +244,7 @@ pub async fn remove_package(
id,
version,
sighash,
force,
signer,
}: RemovePackageParams,
) -> Result<bool, Error> {
@@ -256,7 +260,8 @@ pub async fn remove_package(
let rev = ctx
.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
.as_index()
.as_package()
@@ -266,28 +271,61 @@ pub async fn remove_package(
.as_authorized()
.de()?
.get(&signer_guid)
.map_or(false, |v| version.satisfies(v))
{
if let Some(package) = db
.as_index_mut()
.as_package_mut()
.as_packages_mut()
.as_idx_mut(&id)
{
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())
})?
.map_or(false, |v| {
version
.as_ref()
.map_or(true, |version| version.satisfies(v))
});
if is_authorized {
if let Some(version) = &version {
if let Some(package) = db
.as_index_mut()
.as_package_mut()
.as_packages_mut()
.as_idx_mut(&id)
{
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 {
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(())
} else {

View File

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