Skip to content

Commit da7531d

Browse files
committed
Have rustsec_advisories display expected readme contents
1 parent e657bed commit da7531d

8 files changed

Lines changed: 105 additions & 17 deletions

File tree

rustsec_util/Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rustsec_util/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ publish = false
88
[dependencies]
99
anyhow = { version = "1.0", features = ["backtrace"] }
1010
cargo_metadata = "0.18"
11+
chrono = "0.4"
1112
log = "0.4"
1213
octocrab = "0.34"
1314
once_cell = "1.19"

rustsec_util/src/bin/rustsec_advisories.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use anyhow::{ensure, Context, Result};
22
use cargo_metadata::MetadataCommand;
3+
use chrono::Utc;
34
use once_cell::sync::Lazy;
45
use regex::Regex;
56
use rustsec::{advisory::Informational, Advisory, Database};
67
use rustsec_util::{cargo_unmaintained, command_output, display_advisory_outcomes, Outcome};
7-
use std::{io::Write, path::Path, process::Command};
8+
use std::{env::var, io::Write, path::Path, process::Command};
89
use strum_macros::{Display, EnumIter};
910

1011
// smoelius: See comment in rustsec_issues.rs regarding "../../../".
1112
#[path = "../../../src/packaging.rs"]
1213
mod packaging;
1314
use packaging::temp_package;
1415

15-
#[derive(Display, EnumIter, Eq, PartialEq)]
16+
#[derive(Clone, Copy, Display, EnumIter, Eq, PartialEq)]
1617
#[strum(serialize_all = "kebab_case")]
1718
enum Reason {
1819
Error,
@@ -118,17 +119,62 @@ fn main() -> Result<()> {
118119

119120
display_advisory_outcomes(
120121
&advisory_outcomes
121-
.into_iter()
122+
.iter()
122123
.map(|(advisory, outcome)| {
123-
let url = advisory_url(&advisory);
124-
(advisory.metadata.package, url, outcome)
124+
let url = advisory_url(advisory);
125+
(&advisory.metadata.package, url, *outcome)
125126
})
126127
.collect::<Vec<_>>(),
127128
);
128129

130+
if var("GITHUB_TOKEN_PATH").is_ok() {
131+
println!("---");
132+
display_expected_readme_contents(
133+
&advisory_outcomes
134+
.iter()
135+
.map(|&(_, outcome)| outcome)
136+
.collect::<Vec<_>>(),
137+
);
138+
}
139+
129140
Ok(())
130141
}
131142

143+
macro_rules! count {
144+
($outcomes:expr, $pat:pat) => {
145+
$outcomes
146+
.iter()
147+
.filter(|outcome| matches!(outcome, $pat))
148+
.count()
149+
};
150+
}
151+
152+
fn display_expected_readme_contents(outcomes: &[Outcome<Reason>]) {
153+
let today = Utc::now().date_naive();
154+
let count = outcomes.len();
155+
let found = count!(outcomes, Outcome::Found);
156+
let not_found = count!(outcomes, Outcome::NotFound(_));
157+
let error = count!(outcomes, Outcome::NotFound(Reason::Error));
158+
let leaf = count!(outcomes, Outcome::NotFound(Reason::Leaf));
159+
let recently_updated = count!(outcomes, Outcome::NotFound(Reason::RecentlyUpdated));
160+
let other = count!(outcomes, Outcome::NotFound(Reason::Other));
161+
assert!(found * 3 > count * 2);
162+
println!(
163+
"As of {today}, the RustSec Advisory Database contains {count} active advisories for \
164+
unmaintained packages. Using the above conditions, `cargo-unmaintained` automatically \
165+
identifies {found} of them (more than two thirds). These results can be reproduced by \
166+
running the [`rustsec_advisories`] binary within this repository.",
167+
);
168+
println!(
169+
"- Of the {not_found} packages in the RustSec Advisory Database _not_ identified by \
170+
`cargo-unmaintained`:"
171+
);
172+
println!(" - {error} do not build");
173+
println!(" - {leaf} are existent, unarchived leaves");
174+
println!(" - {recently_updated} were updated within the past 365 days");
175+
println!(" - {other} were not identified for other reasons",);
176+
}
177+
132178
fn advisory_url(advisory: &Advisory) -> String {
133179
format!("https://rustsec.org/advisories/{}.html", advisory.id())
134180
}

rustsec_util/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct Output {
1616
pub stderr: String,
1717
}
1818

19-
#[derive(Eq, PartialEq)]
19+
#[derive(Clone, Copy, Eq, PartialEq)]
2020
pub enum Outcome<T> {
2121
NotFound(T),
2222
Found,

tests/ci.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use similar_asserts::SimpleDiff;
44
use std::{env::remove_var, fs::read_to_string, path::Path};
55
use tempfile::tempdir;
66

7+
mod util;
8+
use util::split_at_cut_line;
9+
710
static DIRS: &[&str] = &[".", "rustsec_util"];
811

912
#[ctor::ctor]
@@ -157,6 +160,23 @@ fn prettier() {
157160
.success();
158161
}
159162

163+
#[test]
164+
fn readme_contains_expected_contents() {
165+
let readme = read_to_string("README.md").unwrap();
166+
let contents = read_to_string("tests/rustsec_advisories.with_token.stdout").unwrap();
167+
let expected_contents = below_cut_line(&contents).unwrap();
168+
for expected_line in expected_contents.lines() {
169+
assert!(
170+
readme.lines().any(|line| line == expected_line),
171+
"failed to find line:\n```\n{expected_line}\n```",
172+
);
173+
}
174+
}
175+
176+
fn below_cut_line(s: &str) -> Option<&str> {
177+
split_at_cut_line(s).map(|(_, below)| below)
178+
}
179+
160180
#[test]
161181
fn readme_contains_usage() {
162182
let readme = read_to_string("README.md").unwrap();

tests/rustsec_advisories.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use snapbox::{assert_matches, Data};
2-
use std::{env::remove_var, path::PathBuf, process::Command};
1+
use snapbox::assert_matches;
2+
use std::{env::remove_var, fs::read_to_string, process::Command};
33

44
mod util;
5-
use util::{tee, token_modifier, Tee};
5+
use util::{split_at_cut_line, tee, token_modifier, Tee};
66

77
#[ctor::ctor]
88
fn initialize() {
@@ -19,16 +19,19 @@ fn rustsec_advisories() {
1919

2020
let output = tee(command, Tee::Stdout).unwrap();
2121

22+
let stdout_expected = read_to_string(format!(
23+
"tests/rustsec_advisories.{}.stdout",
24+
token_modifier()
25+
))
26+
.unwrap();
2227
let stdout_actual = std::str::from_utf8(&output.captured).unwrap();
2328

2429
assert_matches(
25-
Data::read_from(
26-
&PathBuf::from(format!(
27-
"tests/rustsec_advisories.{}.stdout",
28-
token_modifier()
29-
)),
30-
None,
31-
),
32-
stdout_actual,
30+
above_cut_line(&stdout_expected),
31+
above_cut_line(stdout_actual),
3332
);
3433
}
34+
35+
fn above_cut_line(s: &str) -> &str {
36+
split_at_cut_line(s).map_or(s, |(above, _)| above)
37+
}

tests/rustsec_advisories.with_token.stdout

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,10 @@ found (65)
226226
safemem https://rustsec.org/advisories/RUSTSEC-2023-0081.html
227227
generational-arena https://rustsec.org/advisories/RUSTSEC-2024-0014.html
228228
filesystem https://rustsec.org/advisories/RUSTSEC-2024-0015.html
229+
---
230+
As of 2024-02-22, the RustSec Advisory Database contains 92 active advisories for unmaintained packages. Using the above conditions, `cargo-unmaintained` automatically identifies 65 of them (more than two thirds). These results can be reproduced by running the [`rustsec_advisories`] binary within this repository.
231+
- Of the 27 packages in the RustSec Advisory Database _not_ identified by `cargo-unmaintained`:
232+
- 7 do not build
233+
- 3 are existent, unarchived leaves
234+
- 3 were updated within the past 365 days
235+
- 14 were not identified for other reasons

tests/util.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,11 @@ pub fn token_modifier() -> &'static str {
7171
pub fn enabled(key: &str) -> bool {
7272
var(key).map_or(false, |value| value != "0")
7373
}
74+
75+
#[must_use]
76+
pub fn split_at_cut_line(s: &str) -> Option<(&str, &str)> {
77+
const CUT_LINE: &str = "\n---\n";
78+
// smoelius: Preserve initial newline.
79+
s.find(CUT_LINE)
80+
.map(|i| (&s[..=i], &s[i + CUT_LINE.len()..]))
81+
}

0 commit comments

Comments
 (0)