rustsec_admin/commands/
sync.rs1use crate::{prelude::*, synchronizer::Synchronizer};
4use abscissa_core::{Command, Runnable};
5use clap::Parser;
6use std::{
7 path::{Path, PathBuf},
8 process::exit,
9};
10
11#[derive(Command, Debug, Default, Parser)]
13pub struct SyncCmd {
14 #[arg(
16 num_args = 1..,
17 help = "filesystem path to the RustSec advisory DB git repo"
18 )]
19 path: Vec<PathBuf>,
20
21 #[clap(
29 long = "osv",
30 help = "filesystem path to the OSV crates.io data export"
31 )]
32 osv: PathBuf,
33}
34
35impl Runnable for SyncCmd {
36 fn run(&self) {
37 let repo_path = match self.path.len() {
38 0 => Path::new("."),
39 1 => self.path[0].as_path(),
40 _ => unreachable!(),
41 };
42
43 let mut synchronizer = Synchronizer::new(repo_path, &self.osv).unwrap_or_else(|e| {
44 status_err!(
45 "error loading advisory DB repo from {}: {}",
46 repo_path.display(),
47 e
48 );
49
50 exit(1);
51 });
52
53 let advisories = synchronizer.advisory_db().iter();
54
55 if advisories.len() == 0 {
57 status_err!("no advisories found!");
58 exit(1);
59 }
60
61 status_ok!(
62 "Loaded",
63 "{} security advisories (from {})",
64 advisories.len(),
65 repo_path.display()
66 );
67
68 let (updated, mut new) = synchronizer.sync().unwrap_or_else(|e| {
69 status_err!(
70 "error synchronizing advisory DB {}: {}",
71 repo_path.display(),
72 e
73 );
74
75 exit(1);
76 });
77
78 if new.is_empty() {
79 status_ok!("Success", "no new advisories to import");
80 } else {
81 status_ok!("Success", "{} aliases are missing in RustSec", new.len());
82 new.sort_by(|a, b| a.published().partial_cmp(b.published()).unwrap());
85 for a in new {
86 println!(
87 "{:.10}: https://github.com/advisories/{} for {:?}",
88 a.published(),
89 a.id(),
90 a.crates()
91 );
92 }
93 }
94
95 if updated == 0 {
96 status_ok!("Success", "all advisories are up to date");
97 } else {
98 status_ok!("Success", "{} advisories have been updated", updated);
99 }
100 }
101}