Skip to content

Commit 53e6087

Browse files
committed
Add import tool for missing fields in the database
1 parent a7da4a1 commit 53e6087

File tree

3 files changed

+114
-1
lines changed

3 files changed

+114
-1
lines changed

src/admin/import_cksum.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use std::{
2+
fs::File,
3+
io::{BufRead, BufReader},
4+
thread,
5+
time::Duration,
6+
};
7+
8+
use cargo_registry_index::{Repository, RepositoryConfig};
9+
use diesel::prelude::*;
10+
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
11+
12+
use crate::{
13+
admin::dialoguer,
14+
db,
15+
schema::{crates, dependencies, versions},
16+
};
17+
18+
#[derive(clap::Parser, Debug, Copy, Clone)]
19+
#[clap(
20+
name = "import-cksum",
21+
about = "Import missing fields from git into the database"
22+
)]
23+
pub struct Opts {
24+
/// Time in milliseconds to sleep between crate updates to reduce database load.
25+
#[clap(long)]
26+
pause_millis: u64,
27+
}
28+
29+
pub fn run(opts: Opts) -> anyhow::Result<()> {
30+
let conn = db::oneoff_connection().unwrap();
31+
println!("fetching git repo");
32+
let config = RepositoryConfig::from_environment();
33+
let repo = Repository::open(&config)?;
34+
repo.reset_head()?;
35+
println!("HEAD is at {}", repo.head_oid()?);
36+
37+
let files = repo.get_files_modified_since(None)?;
38+
println!("found {} crates", files.len());
39+
if !dialoguer::confirm("continue?") {
40+
return Ok(());
41+
}
42+
43+
let pb = ProgressBar::new(files.len() as u64);
44+
pb.set_style(ProgressStyle::with_template("{bar:60} ({pos}/{len}, ETA {eta})").unwrap());
45+
46+
for file in files.iter().progress_with(pb) {
47+
thread::sleep(Duration::from_millis(opts.pause_millis));
48+
49+
let crate_name = file.file_name().unwrap().to_str().unwrap();
50+
let path = repo.index_file(crate_name);
51+
if !path.exists() {
52+
continue;
53+
}
54+
let file = File::open(path)?;
55+
let reader = BufReader::new(file);
56+
for line in reader.lines() {
57+
let krate: cargo_registry_index::Crate = serde_json::from_str(&line?)?;
58+
conn.transaction(|| import_data(&conn, krate))?;
59+
}
60+
}
61+
62+
Ok(())
63+
}
64+
65+
fn import_data(conn: &PgConnection, krate: cargo_registry_index::Crate) -> QueryResult<()> {
66+
let (version_id, checksum): (i32, Option<String>) = versions::table
67+
.inner_join(crates::table)
68+
.filter(crates::name.eq(&krate.name))
69+
.filter(versions::num.eq(&krate.vers))
70+
.select((versions::id, versions::checksum))
71+
.first(conn)?;
72+
73+
if checksum.is_none() {
74+
// Update the `checksum` and `links` fields.
75+
diesel::update(versions::table)
76+
.set((
77+
versions::checksum.eq(&krate.cksum),
78+
versions::links.eq(&krate.links),
79+
))
80+
.filter(versions::id.eq(version_id))
81+
.execute(conn)?;
82+
// Update the `explicit_name` field for each dependency.
83+
for dep in &krate.deps {
84+
if let Some(package) = &dep.package {
85+
// This is a little tricky because there can be two identical deps in the
86+
// database. The only difference in git is the field we're trying to
87+
// fill (explicit_name). Using `first` here & filtering out existing `explicit_name`
88+
// entries ensure that we assign one explicit_name to each dep.
89+
let id: i32 = dependencies::table
90+
.inner_join(crates::table)
91+
.filter(dependencies::explicit_name.is_null())
92+
.filter(dependencies::version_id.eq(version_id))
93+
.filter(dependencies::req.eq(&dep.req))
94+
.filter(dependencies::features.eq(&dep.features))
95+
.filter(dependencies::optional.eq(&dep.optional))
96+
.filter(dependencies::default_features.eq(&dep.default_features))
97+
.filter(dependencies::target.is_not_distinct_from(&dep.target))
98+
.filter(dependencies::kind.eq(dep.kind.map(|k| k as i32).unwrap_or_default()))
99+
.filter(crates::name.eq(package))
100+
.select(dependencies::id)
101+
.first(conn)?;
102+
diesel::update(dependencies::table)
103+
.set(dependencies::explicit_name.eq(&dep.name))
104+
.filter(dependencies::id.eq(id))
105+
.execute(conn)?;
106+
}
107+
}
108+
}
109+
Ok(())
110+
}

src/admin/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod delete_crate;
22
pub mod delete_version;
33
pub mod dialoguer;
4+
pub mod import_cksum;
45
pub mod migrate;
56
pub mod on_call;
67
pub mod populate;

src/bin/crates-admin.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![warn(clippy::all, rust_2018_idioms)]
22

33
use cargo_registry::admin::{
4-
delete_crate, delete_version, migrate, populate, render_readmes, test_pagerduty,
4+
delete_crate, delete_version, import_cksum, migrate, populate, render_readmes, test_pagerduty,
55
transfer_crates, upload_index, verify_token, yank_version,
66
};
77

@@ -24,6 +24,7 @@ enum SubCommand {
2424
Migrate(migrate::Opts),
2525
UploadIndex(upload_index::Opts),
2626
YankVersion(yank_version::Opts),
27+
ImportCksum(import_cksum::Opts),
2728
}
2829

2930
fn main() -> anyhow::Result<()> {
@@ -42,6 +43,7 @@ fn main() -> anyhow::Result<()> {
4243
SubCommand::Migrate(opts) => migrate::run(opts)?,
4344
SubCommand::UploadIndex(opts) => upload_index::run(opts)?,
4445
SubCommand::YankVersion(opts) => yank_version::run(opts),
46+
SubCommand::ImportCksum(opts) => import_cksum::run(opts)?,
4547
}
4648

4749
Ok(())

0 commit comments

Comments
 (0)