WIP keyring
This commit is contained in:
parent
7dfcb2ad95
commit
e581727d23
9 changed files with 989 additions and 53 deletions
931
Cargo.lock
generated
931
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@ clap_derive = "4.5.4"
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
|
keyring = "2.3.2"
|
||||||
ratatui = "0.26.2"
|
ratatui = "0.26.2"
|
||||||
redact = "0.1.9"
|
redact = "0.1.9"
|
||||||
regex = "1.10.3"
|
regex = "1.10.3"
|
||||||
|
|
13
src/cli.rs
13
src/cli.rs
|
@ -13,7 +13,7 @@ pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Commands,
|
pub command: Commands,
|
||||||
|
|
||||||
/// The directory to write log files
|
/// The directory to write log files (defaults to $XDG_CACHE_HOME/taskr/logs)
|
||||||
#[arg(long, default_value = None)]
|
#[arg(long, default_value = None)]
|
||||||
pub logs_directory: Option<String>,
|
pub logs_directory: Option<String>,
|
||||||
|
|
||||||
|
@ -22,6 +22,10 @@ pub struct Cli {
|
||||||
/// for details
|
/// for details
|
||||||
#[arg(long, default_value = None)]
|
#[arg(long, default_value = None)]
|
||||||
pub tracing_env_filter: Option<String>,
|
pub tracing_env_filter: Option<String>,
|
||||||
|
|
||||||
|
/// The directory that data is stored in (defaults to $XDG_DATA_HOME/taskr/data)
|
||||||
|
#[arg(long, default_value = None)]
|
||||||
|
pub data_directory: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
@ -63,8 +67,7 @@ mod list {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub async fn list(&self) -> Result<()> {
|
pub async fn list(&self, tasks: SharedTasks) -> Result<()> {
|
||||||
let tasks = Arc::new(crate::tasks::Tasks::try_new()?);
|
|
||||||
if self.sync {
|
if self.sync {
|
||||||
eprintln!("Syncing...");
|
eprintln!("Syncing...");
|
||||||
tasks.sync().await?;
|
tasks.sync().await?;
|
||||||
|
@ -92,9 +95,9 @@ mod sync {
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub async fn sync(&self) -> Result<()> {
|
pub async fn sync(&self, tasks: SharedTasks) -> Result<()> {
|
||||||
eprintln!("Syncing...");
|
eprintln!("Syncing...");
|
||||||
Arc::new(crate::tasks::Tasks::try_new()?).sync().await?;
|
tasks.sync().await?;
|
||||||
eprintln!("Done!");
|
eprintln!("Done!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ pub enum Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
pub async fn exec(&self) -> Result<()> {
|
pub async fn exec(&self, tasks: SharedTasks) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Command::Me(args) => args.me().await,
|
Command::Me(args) => args.me(tasks).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ impl Command {
|
||||||
pub struct MeArgs {}
|
pub struct MeArgs {}
|
||||||
|
|
||||||
impl MeArgs {
|
impl MeArgs {
|
||||||
pub async fn me(&self) -> Result<()> {
|
pub async fn me(&self, tasks: SharedTasks) -> Result<()> {
|
||||||
let tasks = crate::tasks::Tasks::try_new()?;
|
|
||||||
println!("{:?}", tasks.gitlab.me().await?);
|
println!("{:?}", tasks.gitlab.me().await?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ pub enum Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
pub async fn exec(&self) -> Result<()> {
|
pub async fn exec(&self, tasks: SharedTasks) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Command::Issue(args) => args.issue().await,
|
Command::Issue(args) => args.issue(tasks).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ pub struct IssueArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IssueArgs {
|
impl IssueArgs {
|
||||||
pub async fn issue(&self) -> Result<()> {
|
pub async fn issue(&self, tasks: SharedTasks) -> Result<()> {
|
||||||
let tasks = crate::tasks::Tasks::try_new()?;
|
|
||||||
let issue = tasks.jira.issue(&self.key).await?;
|
let issue = tasks.jira.issue(&self.key).await?;
|
||||||
println!("{issue:?}");
|
println!("{issue:?}");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -23,15 +23,15 @@ async fn main() -> Result<()> {
|
||||||
// https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html
|
// https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html
|
||||||
let _log_guard = observe::setup_logging(cli.logs_directory, cli.tracing_env_filter)?;
|
let _log_guard = observe::setup_logging(cli.logs_directory, cli.tracing_env_filter)?;
|
||||||
|
|
||||||
println!("Starting...");
|
println!("Initializing taskr...");
|
||||||
trace!("Starting...");
|
let tasks = Arc::new(crate::tasks::Tasks::try_new(cli.data_directory)?);
|
||||||
info!("Starting...");
|
|
||||||
let result = match cli.command {
|
let result = match cli.command {
|
||||||
Commands::Sync(args) => args.sync().await,
|
Commands::Sync(args) => args.sync(tasks).await,
|
||||||
Commands::Ui(_args) => tui::run().await,
|
Commands::Ui(_args) => tui::run(tasks).await,
|
||||||
Commands::List(args) => args.list().await,
|
Commands::List(args) => args.list(tasks).await,
|
||||||
Commands::Jira(jira) => jira.exec().await,
|
Commands::Jira(jira) => jira.exec(tasks).await,
|
||||||
Commands::Gitlab(gitlab) => gitlab.exec().await,
|
Commands::Gitlab(gitlab) => gitlab.exec(tasks).await,
|
||||||
};
|
};
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,3 +5,7 @@ pub use regex::Regex;
|
||||||
pub use std::sync::Arc;
|
pub use std::sync::Arc;
|
||||||
pub use tokio::sync::Mutex;
|
pub use tokio::sync::Mutex;
|
||||||
pub use tracing::{debug, error, info, trace, warn};
|
pub use tracing::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
use crate::tasks::Tasks;
|
||||||
|
|
||||||
|
pub type SharedTasks = Arc<Tasks>;
|
||||||
|
|
52
src/tasks.rs
52
src/tasks.rs
|
@ -4,6 +4,7 @@ use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
env,
|
env,
|
||||||
hash::RandomState,
|
hash::RandomState,
|
||||||
|
path::Path,
|
||||||
process::Command,
|
process::Command,
|
||||||
sync::OnceLock,
|
sync::OnceLock,
|
||||||
};
|
};
|
||||||
|
@ -25,17 +26,41 @@ pub struct Tasks {
|
||||||
const MY_DONE_STATUS_CATEGORY_KEY: &str = "done";
|
const MY_DONE_STATUS_CATEGORY_KEY: &str = "done";
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn try_new() -> Result<Self> {
|
pub fn try_new<D>(data_dir: Option<D>) -> Result<Self>
|
||||||
// TODO: cache, use keyring, talk to a daemon, or otherwise cache this safely
|
where
|
||||||
// TODO: or find a way to more-lazily load the token?
|
D: AsRef<Path>,
|
||||||
let gl_token = env::var("GITLAB_TOKEN").or_else(|_| -> Result<String> {
|
{
|
||||||
|
// TODO: find a way to more-lazily load the token?
|
||||||
|
let gitlab_token_entry = keyring::Entry::new("taskr", "gitlab_token")?;
|
||||||
|
|
||||||
|
let gl_token = match gitlab_token_entry.get_password() {
|
||||||
|
Ok(token) => {
|
||||||
|
info!("GitLab token loaded from keyring");
|
||||||
|
token
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let token = match env::var("GITLAB_TOKEN") {
|
||||||
|
Ok(token) => {
|
||||||
|
info!("GitLab token loaded from environment variable GITLAB_TOKEN");
|
||||||
|
token
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
let output = Command::new("pass")
|
let output = Command::new("pass")
|
||||||
.arg("client/divvy/gitlab-glpat")
|
.arg("client/divvy/gitlab-glpat")
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
})?;
|
info!("GitLab token loaded from password-store entry client/divvy/gitlab-glpat");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
info!("GitLab token stored in keyring");
|
||||||
|
gitlab_token_entry.set_password(&token)?;
|
||||||
|
token
|
||||||
|
}
|
||||||
|
};
|
||||||
let gitlab = GitLab::try_new("https://git.hq.bill.com/api/v4", &gl_token)?;
|
let gitlab = GitLab::try_new("https://git.hq.bill.com/api/v4", &gl_token)?;
|
||||||
|
// TODO: ensure the token works?
|
||||||
|
|
||||||
// TODO: cache, use keyring, talk to a daemon, or otherwise cache this safely
|
// TODO: cache, use keyring, talk to a daemon, or otherwise cache this safely
|
||||||
// TODO: or find a way to more-lazily load the token?
|
// TODO: or find a way to more-lazily load the token?
|
||||||
|
@ -48,7 +73,20 @@ impl Tasks {
|
||||||
})?;
|
})?;
|
||||||
let jira = Jira::try_new("https://billcom.atlassian.net", &jira_token)?;
|
let jira = Jira::try_new("https://billcom.atlassian.net", &jira_token)?;
|
||||||
|
|
||||||
let db = sled::open("data/tasks")?;
|
let mut default_data_dir = Default::default();
|
||||||
|
let data_dir = data_dir
|
||||||
|
.as_ref()
|
||||||
|
.map(D::as_ref)
|
||||||
|
.map(Result::Ok)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// really want to avoid calling this so it's put in here to force it to be lazy
|
||||||
|
// also have to shove it in a function-scoped variable so the
|
||||||
|
// reference we create in this closure has the same lifetime as the function
|
||||||
|
default_data_dir =
|
||||||
|
xdg::BaseDirectories::new()?.create_data_directory("taskr/data")?;
|
||||||
|
Ok(default_data_dir.as_ref())
|
||||||
|
})?;
|
||||||
|
let db = sled::open(data_dir)?;
|
||||||
|
|
||||||
Ok(Self { gitlab, jira, db })
|
Ok(Self { gitlab, jira, db })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::task;
|
use crate::task;
|
||||||
use crate::tasks::Tasks;
|
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, KeyCode, KeyEventKind},
|
event::{self, KeyCode, KeyEventKind},
|
||||||
|
@ -13,9 +12,7 @@ use ratatui::{
|
||||||
};
|
};
|
||||||
use std::io::stdout;
|
use std::io::stdout;
|
||||||
|
|
||||||
pub async fn run() -> Result<()> {
|
pub async fn run(t: SharedTasks) -> Result<()> {
|
||||||
let t = Tasks::try_new()?;
|
|
||||||
|
|
||||||
// print!("{ANSI_CLEAR}");
|
// print!("{ANSI_CLEAR}");
|
||||||
// let gitlab_user = tasks.gitlab.me().await?;
|
// let gitlab_user = tasks.gitlab.me().await?;
|
||||||
// info!("{gitlab_user:#?}");
|
// info!("{gitlab_user:#?}");
|
||||||
|
|
Loading…
Reference in a new issue