Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ base64 = "=0.22.1"
bigdecimal = { version = "=0.4.5", features = ["serde"] }
bon = "=2.3.0"
cargo-manifest = "=0.15.2"
colored = "=2.1.0"
crates_io_cdn_logs = { path = "crates/crates_io_cdn_logs" }
crates_io_database = { path = "crates/crates_io_database" }
crates_io_database_dump = { path = "crates/crates_io_database_dump" }
Expand Down
116 changes: 90 additions & 26 deletions src/admin/delete_crate.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use crate::schema::{crate_owners, teams, users};
use crate::schema::crate_downloads;
use crate::worker::jobs;
use crate::{admin::dialoguer, db, schema::crates};
use anyhow::Context;
use colored::Colorize;
use crates_io_worker::BackgroundJob;
use diesel::dsl::sql;
use diesel::sql_types::Text;
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl};
use diesel::sql_types::{Array, BigInt, Text};
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use futures_util::TryStreamExt;
use std::collections::HashMap;
use std::fmt::Display;

#[derive(clap::Parser, Debug)]
#[command(
Expand All @@ -33,39 +36,60 @@
let mut crate_names = opts.crate_names;
crate_names.sort();

let query_result = crates::table
let existing_crates = crates::table
.inner_join(crate_downloads::table)
.filter(crates::name.eq_any(&crate_names))

Check warning on line 41 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L39-L41

Added lines #L39 - L41 were not covered by tests
.select((
crates::name,
crates::id,
sql::<Text>(
"CASE WHEN crate_owners.owner_kind = 1 THEN teams.login ELSE users.gh_login END",
crate_downloads::downloads,
sql::<Array<Text>>(
r#"
ARRAY(
SELECT
CASE WHEN crate_owners.owner_kind = 1 THEN
teams.login
ELSE
users.gh_login
END
FROM crate_owners
LEFT JOIN teams ON teams.id = crate_owners.owner_id
LEFT JOIN users ON users.id = crate_owners.owner_id
WHERE crate_owners.crate_id = crates.id
)
"#,
),
sql::<BigInt>(
// This is an incorrect reverse dependencies query, since it
// includes the `dependencies` rows for all versions, not just
// the "default version" per crate. However, it's good enough
// for our purposes here.
r#"
(
SELECT COUNT(*)
FROM dependencies
WHERE dependencies.crate_id = crates.id
)
"#,

Check warning on line 73 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L45-L73

Added lines #L45 - L73 were not covered by tests
),
))
.left_join(crate_owners::table.on(crate_owners::crate_id.eq(crates::id)))
.left_join(teams::table.on(teams::id.eq(crate_owners::owner_id)))
.left_join(users::table.on(users::id.eq(crate_owners::owner_id)))
.filter(crates::name.eq_any(&crate_names))
.load::<(String, i32, String)>(&mut conn)
.load_stream::<(String, i32, i64, Vec<String>, i64)>(&mut conn)

Check warning on line 76 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L76

Added line #L76 was not covered by tests
.await
.context("Failed to look up crate name from the database")?;

let mut existing_crates: HashMap<String, (i32, Vec<String>)> = HashMap::new();
for (name, id, login) in query_result {
let entry = existing_crates
.entry(name)
.or_insert_with(|| (id, Vec::new()));

entry.1.push(login);
}
.context("Failed to look up crate name from the database")?
.try_fold(
HashMap::new(),
|mut map, (name, id, downloads, owners, rev_deps)| {
map.insert(name, CrateInfo::new(id, downloads, owners, rev_deps));
futures_util::future::ready(Ok(map))
},
)
.await?;

Check warning on line 86 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L78-L86

Added lines #L78 - L86 were not covered by tests

println!("Deleting the following crates:");
println!();
for name in &crate_names {
match existing_crates.get(name) {
Some((id, owners)) => {
let owners = owners.join(", ");
println!(" - {name} (id={id}, owners={owners})");
}
Some(info) => println!(" - {} ({info})", name.bold()),

Check warning on line 92 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L92

Added line #L92 was not covered by tests
None => println!(" - {name} (⚠️ crate not found)"),
}
}
Expand All @@ -78,7 +102,9 @@
}

for name in &crate_names {
if let Some((id, _)) = existing_crates.get(name) {
if let Some(crate_info) = existing_crates.get(name) {
let id = crate_info.id;

Check warning on line 107 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L105-L107

Added lines #L105 - L107 were not covered by tests
info!("{name}: Deleting crate from the database…");
if let Err(error) = diesel::delete(crates::table.find(id))
.execute(&mut conn)
Expand Down Expand Up @@ -110,3 +136,41 @@

Ok(())
}

#[derive(Debug, Clone)]
struct CrateInfo {
id: i32,
downloads: i64,
owners: Vec<String>,
rev_deps: i64,
}

impl CrateInfo {
pub fn new(id: i32, downloads: i64, owners: Vec<String>, rev_deps: i64) -> Self {
Self {
id,
downloads,
owners,
rev_deps,
}
}

Check warning on line 156 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L149-L156

Added lines #L149 - L156 were not covered by tests
}

impl Display for CrateInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let id = self.id;
let owners = self.owners.join(", ");

write!(f, "id={id}, owners={owners}")?;
if self.downloads > 5000 {
let downloads = format!("downloads={}", self.downloads).bright_red().bold();
write!(f, ", {downloads}")?;
}
if self.rev_deps > 0 {
let rev_deps = format!("rev_deps={}", self.rev_deps).bright_red().bold();
write!(f, ", {rev_deps}")?;
}

Check warning on line 172 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L160-L172

Added lines #L160 - L172 were not covered by tests

Ok(())
}

Check warning on line 175 in src/admin/delete_crate.rs

View check run for this annotation

Codecov / codecov/patch

src/admin/delete_crate.rs#L174-L175

Added lines #L174 - L175 were not covered by tests
}