Skip to content

Commit 9a529aa

Browse files
committed
Add --save-token option
1 parent 951fd06 commit 9a529aa

9 files changed

Lines changed: 164 additions & 14 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ walkdir = "2.5"
3131

3232
[target.'cfg(unix)'.dependencies]
3333
libc = { version = "0.2", optional = true }
34-
xdg = { version = "2.5", optional = true }
34+
xdg = { version = "2.5" }
3535

3636
[target.'cfg(windows)'.dependencies]
3737
windows-sys = { version = "0.59", features = [
@@ -52,7 +52,7 @@ snapbox = "0.6"
5252
[features]
5353
default = ["on-disk-cache", "lock-index"]
5454
cache-repositories = ["on-disk-cache"]
55-
on-disk-cache = ["xdg"]
55+
on-disk-cache = []
5656
lock-index = ["libc", "windows-sys"]
5757

5858
[lints.rust.unexpected_cfgs]

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Options:
6868
--no-exit-code Do not set exit status when unmaintained packages are found
6969
--no-warnings Do not show warnings
7070
-p, --package <NAME> Check only whether package NAME is unmaintained
71+
--save-token Read a personal access token from standard input and save it to
72+
$HOME/.config/cargo-unmaintained/token.txt
7173
--tree Show paths to unmaintained packages
7274
--verbose Show information about what cargo-unmaintained is doing
7375
-h, --help Print help
@@ -77,6 +79,13 @@ The `GITHUB_TOKEN_PATH` environment variable can be set to the path of a file co
7779
access token. If set, cargo-unmaintained will use this token to authenticate to GitHub and check
7880
whether packages' repositories have been archived.
7981
82+
Alternatively, the `GITHUB_TOKEN` environment variable can be set to a personal access token.
83+
However, use of `GITHUB_TOKEN_PATH` is recommended as it is less likely to leak the token.
84+
85+
If neither `GITHUB_TOKEN_PATH` nor `GITHUB_TOKEN` is set, but a file exists at
86+
$HOME/.config/cargo-unmaintained/token.txt, cargo-unmaintained will use that file's contents as a
87+
personal access token.
88+
8089
Unless --no-exit-code is passed, the exit status is 0 if no unmaintained packages were found and no
8190
irrecoverable errors occurred, 1 if unmaintained packages were found, and 2 if an irrecoverable
8291
error occurred.

build.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
1+
use std::{env::var, fs::write, path::PathBuf};
2+
3+
const TOKEN_PATH: &str = if cfg!(not(windows)) {
4+
"$HOME/.config/cargo-unmaintained/token.txt"
5+
} else {
6+
"%LOCALAPPDATA%\\\\cargo-unmaintained\\\\token.txt"
7+
};
8+
19
fn main() {
210
println!("cargo:rustc-cfg=__warnings");
311

412
#[cfg(all(feature = "cache-repositories", not(feature = "on-disk-cache")))]
513
println!("cargo:warning=Feature `cache-repositories` has been renamed to `on-disk-cache`");
14+
15+
let out_dir = var("OUT_DIR").unwrap();
16+
let path = PathBuf::from(out_dir).join("after_help.rs");
17+
let contents = format!(
18+
r#"const AFTER_HELP: &str = "\
19+
The `GITHUB_TOKEN_PATH` environment variable can be set to the path of a file containing a \
20+
personal access token. If set, cargo-unmaintained will use this token to authenticate to GitHub \
21+
and check whether packages' repositories have been archived.
22+
23+
Alternatively, the `GITHUB_TOKEN` environment variable can be set to a personal access token. \
24+
However, use of `GITHUB_TOKEN_PATH` is recommended as it is less likely to leak the token.
25+
26+
If neither `GITHUB_TOKEN_PATH` nor `GITHUB_TOKEN` is set, but a file exists at {TOKEN_PATH}, \
27+
cargo-unmaintained will use that file's contents as a personal access token.
28+
29+
Unless --no-exit-code is passed, the exit status is 0 if no unmaintained packages were found and \
30+
no irrecoverable errors occurred, 1 if unmaintained packages were found, and 2 if an irrecoverable \
31+
error occurred.";
32+
"#,
33+
);
34+
write(path, contents).unwrap();
635
}

rustsec_util/Cargo.lock

Lines changed: 7 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ strum_macros = "0.26"
1919
tempfile = "3.13"
2020
tokio = "1.40"
2121

22+
[target.'cfg(unix)'.dependencies]
23+
xdg = { version = "2.5" }
24+
2225
[lints.rust.unexpected_cfgs]
2326
level = "deny"
2427
check-cfg = ["cfg(dylint_lib, values(any()))", "cfg(__warnings)"]

rustsec_util/src/bin/rustsec_issues/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod octocrab_util;
1818
mod flush;
1919
use flush::Flush;
2020

21+
#[allow(unused)]
2122
#[path = "../../../../src/github/util.rs"]
2223
mod github_util;
2324

src/github/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ mod map_ext;
1515
use map_ext::MapExt;
1616

1717
mod util;
18-
pub(crate) use util::load_token;
1918
use util::PERSONAL_TOKEN;
19+
pub(crate) use util::{load_token, save_token};
2020

2121
#[allow(clippy::unwrap_used)]
2222
static RE: Lazy<Regex> =

src/github/util.rs

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
use anyhow::{anyhow, Context, Result};
2-
use std::{env::var, fs::read_to_string, sync::OnceLock};
2+
use once_cell::sync::Lazy;
3+
use std::{
4+
env::var,
5+
fs::{create_dir_all, read_to_string, File, OpenOptions},
6+
io::{stdin, Write},
7+
path::PathBuf,
8+
sync::OnceLock,
9+
};
10+
11+
#[allow(clippy::unwrap_used)]
12+
static CONFIG_DIRECTORY: Lazy<PathBuf> = Lazy::new(|| {
13+
#[cfg(not(windows))]
14+
{
15+
let base_directories = xdg::BaseDirectories::new().unwrap();
16+
base_directories.get_config_file("cargo-unmaintained")
17+
}
18+
#[cfg(windows)]
19+
{
20+
let local_app_data = var("LOCALAPPDATA").unwrap();
21+
PathBuf::from(local_app_data).join("cargo-unmaintained")
22+
}
23+
});
24+
25+
static TOKEN_PATH: Lazy<PathBuf> = Lazy::new(|| CONFIG_DIRECTORY.join("token.txt"));
326

427
pub(super) static PERSONAL_TOKEN: OnceLock<String> = OnceLock::new();
528

@@ -16,11 +39,19 @@ pub(crate) fn load_token(f: impl FnOnce(String) -> Result<()>) -> Result<bool> {
1639
);
1740
}
1841
token
42+
} else if TOKEN_PATH.try_exists().with_context(|| {
43+
format!(
44+
"failed to determine whether `{}` exists",
45+
TOKEN_PATH.display()
46+
)
47+
})? {
48+
read_to_string(&*TOKEN_PATH).with_context(|| format!("failed to read {TOKEN_PATH:?}"))?
1949
} else {
2050
#[cfg(__warnings)]
2151
crate::warn!(
22-
"`GITHUB_TOKEN_PATH` and `GITHUB_TOKEN` are not set; archival statuses will not be \
23-
checked",
52+
"`GITHUB_TOKEN_PATH` and `GITHUB_TOKEN` are not set and no file was found at {}; \
53+
archival statuses will not be checked",
54+
TOKEN_PATH.display()
2455
);
2556
return Ok(false);
2657
};
@@ -31,3 +62,56 @@ pub(crate) fn load_token(f: impl FnOnce(String) -> Result<()>) -> Result<bool> {
3162
f(token)?;
3263
Ok(true)
3364
}
65+
66+
pub(crate) fn save_token() -> Result<()> {
67+
println!("Please paste a personal access token below. The token needs no scopes.");
68+
69+
let mut buf = String::new();
70+
71+
{
72+
let n = stdin()
73+
.read_line(&mut buf)
74+
.with_context(|| "failed to read stdin")?;
75+
assert_eq!(buf.len(), n);
76+
}
77+
78+
create_dir_all(&*CONFIG_DIRECTORY).with_context(|| "failed to create config directory")?;
79+
80+
let mut file = OpenOptions::new()
81+
.create(true)
82+
.truncate(true)
83+
.write(true)
84+
.open(&*TOKEN_PATH)
85+
.with_context(|| format!("failed to open `{}`", TOKEN_PATH.display()))?;
86+
set_permissions(&file, 0o600)?;
87+
file.write_all(buf.as_bytes())
88+
.with_context(|| format!("failed to write `{}`", TOKEN_PATH.display()))?;
89+
90+
println!(
91+
"Personal access token written to `{}`",
92+
TOKEN_PATH.display()
93+
);
94+
95+
Ok(())
96+
}
97+
98+
type CargoResult<T> = Result<T>;
99+
100+
// smoelius: The below definitions of `set_permissions` were copied from:
101+
// https://github.com/rust-lang/cargo/blob/1e6828485eea0f550ed7be46ef96107b46aeb162/src/cargo/util/config.rs#L1010-L1024
102+
#[cfg(unix)]
103+
#[cfg_attr(dylint_lib = "try_io_result", allow(try_io_result))]
104+
fn set_permissions(file: &File, mode: u32) -> CargoResult<()> {
105+
use std::os::unix::fs::PermissionsExt;
106+
107+
let mut perms = file.metadata()?.permissions();
108+
perms.set_mode(mode);
109+
file.set_permissions(perms)?;
110+
Ok(())
111+
}
112+
113+
#[cfg(not(unix))]
114+
#[allow(unused, clippy::unnecessary_wraps)]
115+
fn set_permissions(file: &File, mode: u32) -> CargoResult<()> {
116+
Ok(())
117+
}

src/main.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,15 @@ enum CargoSubCommand {
5959
Unmaintained(Opts),
6060
}
6161

62+
include!(concat!(env!("OUT_DIR"), "/after_help.rs"));
63+
6264
#[allow(clippy::struct_excessive_bools)]
6365
#[derive(Debug, Parser)]
6466
#[remain::sorted]
6567
#[clap(
6668
version = crate_version!(),
6769
about = "Find unmaintained packages in Rust projects",
68-
after_help = "\
69-
The `GITHUB_TOKEN_PATH` environment variable can be set to the path of a file containing a \
70-
personal access token. If set, cargo-unmaintained will use this token to authenticate to GitHub \
71-
and check whether packages' repositories have been archived.
72-
73-
Unless --no-exit-code is passed, the exit status is 0 if no unmaintained packages were found and \
74-
no irrecoverable errors occurred, 1 if unmaintained packages were found, and 2 if an irrecoverable \
75-
error occurred."
70+
after_help = AFTER_HELP
7671
)]
7772
struct Opts {
7873
#[clap(
@@ -122,6 +117,22 @@ struct Opts {
122117
)]
123118
package: Option<String>,
124119

120+
#[cfg(not(windows))]
121+
#[clap(
122+
long,
123+
help = "Read a personal access token from standard input and save it to \
124+
$HOME/.config/cargo-unmaintained/token.txt"
125+
)]
126+
save_token: bool,
127+
128+
#[cfg(windows)]
129+
#[clap(
130+
long,
131+
help = "Read a personal access token from standard input and save it to \
132+
%LOCALAPPDATA%\\cargo-unmaintained\\token.txt"
133+
)]
134+
save_token: bool,
135+
125136
#[clap(long, help = "Show paths to unmaintained packages")]
126137
tree: bool,
127138

@@ -214,6 +225,12 @@ fn main() -> Result<()> {
214225

215226
opts::init(opts);
216227

228+
if opts::get().save_token {
229+
// smoelius: Currently, if additional options are passed besides --save-token, they are
230+
// ignored and no error is emitted. This is ugly.
231+
return github::save_token();
232+
}
233+
217234
if github::load_token(|_| Ok(()))? {
218235
TOKEN_FOUND.store(true, Ordering::SeqCst);
219236
}

0 commit comments

Comments
 (0)