cargo_unmaintained/github/real/
util.rs1use anyhow::{Result, anyhow};
2use elaborate::std::{
3 env::var_wc,
4 fs::{OpenOptionsContext, create_dir_all_wc, read_to_string_wc},
5 io::{StdinContext, WriteContext},
6 path::PathContext,
7};
8use std::{
9 fs::{File, OpenOptions},
10 io::stdin,
11 path::PathBuf,
12 sync::{LazyLock, OnceLock},
13};
14
15#[allow(clippy::unwrap_used)]
16static CONFIG_DIRECTORY: LazyLock<PathBuf> = LazyLock::new(|| {
17 #[cfg(not(windows))]
18 {
19 let base_directories = xdg::BaseDirectories::new();
20 base_directories
21 .create_config_directory("cargo-unmaintained")
22 .unwrap()
23 }
24 #[cfg(windows)]
25 {
26 let local_app_data = var_wc("LOCALAPPDATA").unwrap();
27 PathBuf::from(local_app_data).join("cargo-unmaintained")
28 }
29});
30
31static TOKEN_PATH: LazyLock<PathBuf> = LazyLock::new(|| CONFIG_DIRECTORY.join("token.txt"));
32
33pub(super) static PERSONAL_TOKEN: OnceLock<String> = OnceLock::new();
34
35pub fn load_token(f: impl FnOnce(&str) -> Result<()>) -> Result<bool> {
36 let token_untrimmed = if let Ok(path) = var_wc("GITHUB_TOKEN_PATH") {
37 read_to_string_wc(&path)?
38 } else if let Ok(token) = var_wc("GITHUB_TOKEN") {
39 if var_wc("CI").is_err() {
41 #[cfg(__warnings)]
42 crate::warn!(
43 "found a token in `GITHUB_TOKEN`; consider using the more secure method of \
44 setting `GITHUB_TOKEN_PATH` to the path of a file containing the token",
45 );
46 }
47 token
48 } else if TOKEN_PATH.try_exists_wc()? {
49 read_to_string_wc(&*TOKEN_PATH)?
50 } else {
51 #[cfg(__warnings)]
52 crate::warn!(
53 "`GITHUB_TOKEN_PATH` and `GITHUB_TOKEN` are not set and no file was found at {}; \
54 archival statuses will not be checked",
55 TOKEN_PATH.display()
56 );
57 return Ok(false);
58 };
59 let token = token_untrimmed.trim_end().to_owned();
60 PERSONAL_TOKEN
61 .set(token.clone())
62 .map_err(|_| anyhow!("`load_token` was already called"))?;
63 f(&token)?;
64 Ok(true)
65}
66
67pub(crate) fn save_token() -> Result<()> {
68 println!("Please paste a personal access token below. The token needs no scopes.");
69
70 let mut buf = String::new();
71
72 {
73 let n = stdin().read_line_wc(&mut buf)?;
74 assert_eq!(buf.len(), n);
75 }
76
77 create_dir_all_wc(&*CONFIG_DIRECTORY)?;
78
79 let mut file = OpenOptions::new()
80 .create(true)
81 .truncate(true)
82 .write(true)
83 .open_wc(&*TOKEN_PATH)?;
84 set_permissions(&file, 0o600)?;
85 file.write_all_wc(buf.as_bytes())?;
86
87 println!(
88 "Personal access token written to `{}`",
89 TOKEN_PATH.display()
90 );
91
92 Ok(())
93}
94
95type CargoResult<T> = Result<T>;
96
97#[cfg(unix)]
100#[cfg_attr(dylint_lib = "try_io_result", allow(try_io_result))]
101#[allow(clippy::disallowed_methods)]
102fn set_permissions(file: &File, mode: u32) -> CargoResult<()> {
103 use std::os::unix::fs::PermissionsExt;
104
105 let mut perms = file.metadata()?.permissions();
106 perms.set_mode(mode);
107 file.set_permissions(perms)?;
108 Ok(())
109}
110
111#[cfg(not(unix))]
112#[allow(unused, clippy::disallowed_methods, clippy::unnecessary_wraps)]
113fn set_permissions(file: &File, mode: u32) -> CargoResult<()> {
114 Ok(())
115}