mirror of
https://github.com/nukesor/pueue
synced 2024-11-05 17:50:20 +00:00
Merge branch 'master' into warnings-remove-kill
This commit is contained in:
commit
2ffa264b62
8 changed files with 113 additions and 60 deletions
|
@ -16,11 +16,19 @@ This version adds breaking changes:
|
|||
- Unix socket support (#90)
|
||||
- Warning messages for removing/killing tasks (#111) by [Julian Kaindl](https://github.com/kaindljulian)
|
||||
|
||||
### Changed
|
||||
|
||||
- Never create a default config when starting the client. Only starting the daemon can do that.
|
||||
- Windows: The configuration file will now also be placed in `%APPDATA%\Local\pueue`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed panic, when killing and immediately removing a task. (#119)
|
||||
- Fixed broken non-responsive daemon, on panic in threads. (#119)
|
||||
- Don't allow empty commands on `add`.
|
||||
- The client will never persist/write the configuration file. (#116)
|
||||
- The daemon will only persist configuration file on startup, if anything changes. (#116)
|
||||
- (Probably fixed) Malformed configuration file. (#116)
|
||||
|
||||
## [0.7.2] - 2020-10-05
|
||||
|
||||
|
|
38
README.md
38
README.md
|
@ -246,42 +246,52 @@ SUBCOMMANDS:
|
|||
|
||||
## Configs
|
||||
|
||||
The configuration file of Pueue is located in `$CARGO_HOME/pueue.yml`.
|
||||
The default will be generated after starting pueue once.
|
||||
The configuration file of Pueue is located in these directories:
|
||||
|
||||
- Linux: `$HOME/.config/pueue/pueue.yml`.
|
||||
- MacOs: `$HOME/Library/Preferences/pueue/pueue.yml`
|
||||
- Windows: `%APPDATA%\Local\pueue`
|
||||
|
||||
A default configuration file will be generated after starting `pueued` for the first time.
|
||||
|
||||
```yaml
|
||||
---
|
||||
client:
|
||||
daemon_port: "6924"
|
||||
shared:
|
||||
port: "6924"
|
||||
secret: "your_secret"
|
||||
pueue_directory: /home/$USER/.local/share/pueue
|
||||
use_unix_sockets: false
|
||||
unix_sockets_path: /home/$USER/.local/share/pueue/pueue_$USER.socket
|
||||
client:
|
||||
read_local_logs: true
|
||||
print_remove_warnings: false
|
||||
|
||||
daemon:
|
||||
pueue_directory: /home/$USER/.local/share/pueue
|
||||
default_parallel_tasks: 1
|
||||
pause_on_failure: false
|
||||
port: "6924"
|
||||
secret: "your_secret"
|
||||
callback: ""Task {{ id }}\nCommand: {{ command }}\nPath: {{ path }}\nFinished with status '{{ result }}'\""
|
||||
groups:
|
||||
cpu: 1
|
||||
```
|
||||
|
||||
**Client**:
|
||||
### Shared
|
||||
|
||||
- `daemon_port` The port the client tries to connect to.
|
||||
- `port` The port the daemon listens on and the client connects to in TCP mode.
|
||||
- `secret` The secret, that's used for authentication
|
||||
- `pueue_directory` The location Pueue uses for its intermediate files and logs.
|
||||
- `use_unix_sockets` Whether the daemon should listen on a Unix- or a TCP-socket.
|
||||
- `unix_socket_path` The path the unix socket is located at.
|
||||
|
||||
### Client
|
||||
|
||||
- `read_local_logs` If the client runs as the same user (and on the same machine) as the daemon, logs don't have to be sent via the socket but rather read directly.
|
||||
- `print_remove_warnings` The client will print warnings that require confirmation for different critical commands.
|
||||
|
||||
**Daemon**:
|
||||
### Daemon
|
||||
|
||||
- `pueue_directory` The location Pueue uses for its intermediate files and logs.
|
||||
- `default_parallel_tasks` Determines how many tasks should be processed concurrently.
|
||||
|
||||
- `default_parallel_tasks` Determines how many tasks should be processed concurrently.
|
||||
- `pause_on_failure` If set to `true`, the daemon stops starting new task as soon as a single task fails. Already running tasks will continue.
|
||||
- `port` The port the daemon should listen to.
|
||||
- `secret` The secret, that's used for authentication
|
||||
- `callback` The command that will be called after a task finishes. Can be parameterized
|
||||
- `groups` This is a list of the groups with their amount of allowed parallel tasks. It's advised to not manipulate this manually, but rather use the `group` subcommand to create and remove groups.
|
||||
|
||||
|
|
|
@ -15,28 +15,9 @@ use crate::client::Client;
|
|||
|
||||
#[async_std::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Get settings from the configuration file and the program defaults.
|
||||
let settings = Settings::new()?;
|
||||
// Immediately save it. This also creates the save file in case it didn't exist yet.
|
||||
if let Err(error) = settings.save() {
|
||||
println!("Failed saving config file.");
|
||||
println!("{:?}", error);
|
||||
}
|
||||
|
||||
// Parse commandline options.
|
||||
let opt = Opt::from_args();
|
||||
|
||||
// Set the verbosity level for the client app.
|
||||
if opt.verbose >= 3 {
|
||||
SimpleLogger::init(LevelFilter::Debug, Config::default())?;
|
||||
} else if opt.verbose == 2 {
|
||||
SimpleLogger::init(LevelFilter::Info, Config::default())?;
|
||||
} else if opt.verbose == 1 {
|
||||
SimpleLogger::init(LevelFilter::Warn, Config::default())?;
|
||||
} else if opt.verbose == 0 {
|
||||
SimpleLogger::init(LevelFilter::Error, Config::default())?;
|
||||
}
|
||||
|
||||
if let SubCommand::Completions {
|
||||
shell,
|
||||
output_directory,
|
||||
|
@ -47,6 +28,20 @@ async fn main() -> Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
// Set the verbosity level of the logger.
|
||||
if opt.verbose >= 3 {
|
||||
SimpleLogger::init(LevelFilter::Debug, Config::default())?;
|
||||
} else if opt.verbose == 2 {
|
||||
SimpleLogger::init(LevelFilter::Info, Config::default())?;
|
||||
} else if opt.verbose == 1 {
|
||||
SimpleLogger::init(LevelFilter::Warn, Config::default())?;
|
||||
} else if opt.verbose == 0 {
|
||||
SimpleLogger::init(LevelFilter::Error, Config::default())?;
|
||||
}
|
||||
|
||||
// Try to read settings from the configuration file.
|
||||
let settings = Settings::new(true)?;
|
||||
|
||||
// Create client to talk with the daemon and connect.
|
||||
let mut client = Client::new(settings, opt).await?;
|
||||
client.start().await?;
|
||||
|
|
|
@ -27,14 +27,6 @@ mod task_handler;
|
|||
|
||||
#[async_std::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Get settings from the configuration file and the program defaults.
|
||||
let settings = Settings::new()?;
|
||||
// Immediately save it. This also creates the save file in case it didn't exist yet.
|
||||
if let Err(error) = settings.save() {
|
||||
println!("Failed saving config file.");
|
||||
println!("{:?}", error);
|
||||
}
|
||||
|
||||
// Parse commandline options.
|
||||
let opt = Opt::from_args();
|
||||
|
||||
|
@ -42,7 +34,7 @@ async fn main() -> Result<()> {
|
|||
fork_daemon(&opt)?;
|
||||
}
|
||||
|
||||
// Set the verbosity level for the client app.
|
||||
// Set the verbosity level of the logger.
|
||||
if opt.verbose >= 3 {
|
||||
SimpleLogger::init(LevelFilter::Debug, Config::default())?;
|
||||
} else if opt.verbose == 2 {
|
||||
|
@ -53,6 +45,25 @@ async fn main() -> Result<()> {
|
|||
SimpleLogger::init(LevelFilter::Error, Config::default())?;
|
||||
}
|
||||
|
||||
// Try to read settings from the configuration file.
|
||||
let settings = match Settings::read(false) {
|
||||
Ok(settings) => settings,
|
||||
Err(_) => {
|
||||
// There's something wrong with the config file or something's missing.
|
||||
// Try to read the config and fill missing values with defaults.
|
||||
// This might be possible on version upgrade or first run.
|
||||
let settings = Settings::new(false)?;
|
||||
|
||||
// Since we needed to add values to the configuration, we have to save it.
|
||||
// This also creates the save file in case it didn't exist yet.
|
||||
if let Err(error) = settings.save() {
|
||||
println!("Failed saving config file.");
|
||||
println!("{:?}", error);
|
||||
}
|
||||
settings
|
||||
}
|
||||
};
|
||||
|
||||
init_directories(&settings.shared.pueue_directory);
|
||||
|
||||
let state = State::new(&settings);
|
||||
|
|
|
@ -18,10 +18,14 @@ pub fn compile_shell_command(command_string: &str) -> Command {
|
|||
/// Send a signal to a windows process.
|
||||
pub fn send_signal_to_child(
|
||||
_child: &Child,
|
||||
_action: &ProcessAction,
|
||||
action: &ProcessAction,
|
||||
_children: bool,
|
||||
) -> Result<bool> {
|
||||
bail!("not supported on windows.")
|
||||
match action {
|
||||
ProcessAction::Pause => bail!("Pause is not yet supported on windows."),
|
||||
ProcessAction::Resume => bail!("Resume is not yet supported on windows."),
|
||||
ProcessAction::Kill => bail!("Kill is not yet supported on windows."),
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill a child process
|
||||
|
|
|
@ -44,6 +44,12 @@ pub type Socket = Box<dyn GenericSocket>;
|
|||
/// which depends on the parameters.
|
||||
pub async fn get_client(unix_socket_path: Option<String>, port: Option<String>) -> Result<Socket> {
|
||||
if let Some(socket_path) = unix_socket_path {
|
||||
if !PathBuf::from(&socket_path).exists() {
|
||||
bail!(
|
||||
"Couldn't find unix socket at path {:?}. Is the daemon running yet?",
|
||||
socket_path
|
||||
);
|
||||
}
|
||||
let stream = UnixStream::connect(socket_path).await?;
|
||||
return Ok(Box::new(stream));
|
||||
}
|
||||
|
|
|
@ -12,15 +12,14 @@ fn get_home_dir() -> Result<PathBuf> {
|
|||
}
|
||||
|
||||
pub fn default_config_directory() -> Result<PathBuf> {
|
||||
Ok(get_home_dir()?.join("pueue"))
|
||||
Ok(dirs::data_local_dir()
|
||||
.ok_or(anyhow!("Couldn't resolve app data directory"))?
|
||||
.join("pueue"))
|
||||
}
|
||||
|
||||
pub fn get_config_directories() -> Result<Vec<PathBuf>> {
|
||||
Ok(vec![
|
||||
// Windows Terminal stores its config file in the "AppData/Local" directory.
|
||||
dirs::data_local_dir()
|
||||
.ok_or(anyhow!("Couldn't resolve app data directory"))?
|
||||
.join("pueue"),
|
||||
default_config_directory()?,
|
||||
Path::new(".").to_path_buf(),
|
||||
])
|
||||
|
|
|
@ -2,8 +2,9 @@ use std::collections::HashMap;
|
|||
use std::fs::{create_dir_all, File};
|
||||
use std::io::prelude::*;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use config::Config;
|
||||
use log::info;
|
||||
use rand::Rng;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
|
@ -30,16 +31,11 @@ pub struct Client {
|
|||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Daemon {
|
||||
pub default_parallel_tasks: usize,
|
||||
#[serde(default = "pause_on_failure_default")]
|
||||
pub pause_on_failure: bool,
|
||||
pub callback: Option<String>,
|
||||
pub groups: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
fn pause_on_failure_default() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Settings {
|
||||
pub shared: Shared,
|
||||
|
@ -53,7 +49,9 @@ impl Settings {
|
|||
/// If a local config file already exists it is parsed and
|
||||
/// overwrites the default option values.
|
||||
/// The local config is located at "~/.config/pueue.yml".
|
||||
pub fn new() -> Result<Settings> {
|
||||
///
|
||||
/// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
|
||||
pub fn new(require_config: bool) -> Result<Settings> {
|
||||
let mut config = Config::new();
|
||||
|
||||
config.set_default("shared.port", "6924")?;
|
||||
|
@ -73,7 +71,21 @@ impl Settings {
|
|||
config.set_default("daemon.groups", HashMap::<String, i64>::new())?;
|
||||
|
||||
// Add in the home config file
|
||||
parse_config(&mut config)?;
|
||||
parse_config(&mut config, require_config)?;
|
||||
|
||||
// Try to can deserialize the entire configuration
|
||||
Ok(config.try_into()?)
|
||||
}
|
||||
|
||||
/// Try to read the config file without any default values.
|
||||
/// This is done by the daemon on startup.
|
||||
/// If the file can be read without any need for defaults, we don't have to persist it
|
||||
/// afterwards.
|
||||
pub fn read(require_config: bool) -> Result<Settings> {
|
||||
let mut config = Config::new();
|
||||
|
||||
// Merge configuration files we can find in ascending order.
|
||||
parse_config(&mut config, require_config)?;
|
||||
|
||||
// Try to can deserialize the entire configuration
|
||||
Ok(config.try_into()?)
|
||||
|
@ -103,18 +115,26 @@ impl Settings {
|
|||
/// Get all possible configuration paths and check if there are
|
||||
/// configuration files at those locations.
|
||||
/// All configs will be merged by importance.
|
||||
fn parse_config(settings: &mut Config) -> Result<()> {
|
||||
//println!("Parsing config files");
|
||||
///
|
||||
/// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
|
||||
fn parse_config(settings: &mut Config, require_config: bool) -> Result<()> {
|
||||
let mut config_found = false;
|
||||
info!("Parsing config files");
|
||||
for directory in get_config_directories()?.into_iter() {
|
||||
let path = directory.join("pueue.yml");
|
||||
//println!("Checking path: {:?}", &path);
|
||||
info!("Checking path: {:?}", &path);
|
||||
if path.exists() {
|
||||
//println!("Parsing config file at: {:?}", path);
|
||||
info!("Found config file at: {:?}", path);
|
||||
config_found = true;
|
||||
let config_file = config::File::with_name(path.to_str().unwrap());
|
||||
settings.merge(config_file)?;
|
||||
}
|
||||
}
|
||||
|
||||
if require_config && !config_found {
|
||||
bail!("Couldn't find a configuration file. Did you start the daemon yet?");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue