-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Config file management #7
Comments
The configure crate by @withoutboats abstract about configuration in general (as the name suggests), but doesn't seem to have an adapter for configuration files right now (except for Cargo.toml, but that's not the use case we have in mind I guess). |
I think it'd good to lay out some requirements for a crate(s) that would fill this gap:
I'd be OK with having multiple sub crates for various platforms, and then a "parent" crate that allows abstracting over the platform where all I do is specify a file name I want to load/save. |
Bonus if the crate can allow me to search custom directories, or tweak the order (on platforms where applicable). |
Nope, the configure crate's default "source" is definitely designed for use cases where the person configuring the application is also the author - such as network services. However, the intent is for libraries to use configure, so that the application author can have total control over the source of configuration. A configuration source that integrates with configure and is designed for CLIs would be a great addition, and possibly one I'd be interested in upstreaming into configure proper. |
I'm thinking this issue might be part of a bigger topic.
It's probably uncommon for a single application to use all of these, but one or more should be common enough. It feels like these questions are part of the same problem; perhaps it might be useful to consider all of these questions as part of this discussion? |
For the question of temporary files there is already a crate which seems to do its job quite nicely (though I've only used it in limited scenarios so far, maybe it can be improved!) As for the rest…I think it would be pretty cool if we could create (or find and improve existing) crates that mirror the same behaviour for other configuration, essential and non-essential data files as well. It should be as simple as saying Edit Just as I hit "Comment" I found this crate here |
Hi, @soc! The Rust CLI working group is talking about cross-platform configuration file management and your directories crate has come up. Looking at your Github profile, I see you have a Java directories package as well, so you seem have some expertise in this area. Wanna chime in here? :) |
@killercup Sure, how can I help? |
@soc awesome! We were currently doing some research about the status quo of crates that are useful when writing CLI tools, work cross-platform and are maintained. For example, we want to come up with a good story around how to easily configure a CLI tool—with config files, env vars, and CLI flags. This issue is focussing on the handling of config files. @kbknapp already listed some good requirements in https://github.com/rust-lang-nursery/cli-wg/issues/7#issuecomment-367085114. Do you think directories is a good foundation here? What are your plans for it? Can we help you get it to 1.0? :) (@spacekookie and @yoshuawuyts probably have more to say!) |
directories is intentionally focused solely on dealing with operating system defaults. For instance, when dealing with CLI flags, the first issue you have is that of style (
I do think that directories is a good foundation for dealing with the operating system standards part of your goals. I believe that dealing with CLI flags should probably be done in a separate library, or in a way more specific to the individual application's needs, because dealing with CLI flags is very application-specific. In the end individual applications already need to have some custom code anyway to deal with migrating from storing their data directly in That's why directories only tells developers which directories they should be using, but does not get involved with creating directories itself, or making decisions about the priority of multiple directories (for instance platform defaults vs. CLI flags vs. config files). Application-specific code will be required to handle such issues, and I want directories to avoid getting involved in that: Often the cost of complexity to solve such issues in a general fashion in a library is way higher than dealing with it on the application side, especially when handling (legacy) applications with their own folder in Here is an example of an application that makes use of directories (the JVM version) and deals with migration compatibility, property files, and application-specific env vars: coursier/coursier#676.
My plan is to declare it as stable as fast as possible. I think the main blockers are
|
I have created tickets for the remaining issues I mentioned: dirs-dev/directories-rs#1 and dirs-dev/directories-rs#2. |
A more general note: There is a vast difference between selecting and standardizing on crates that provide certain functionality (like CLI parsing, config file parsing) and having one standardized way of handling application configuration: With the former you probably get crates that do almost everything and allow configuration of almost everything. With the latter, you want to be highly selective and make actual choices how things can be specified, and not allow a free for all in terms of decisions a developer can make. |
As you've noticed, I've opened some issues at directories-rs. I'd hold off on releasing a 1.0 before there are some consumers of the crate.
Absolutely. We already have some great libraries for CLI args, and I'd love to have an equally as good story for dealing with config files. That is not one crate – it's several build on top of and complementing each other :) (We'll hopefully see more concrete proposals for this in #6!) |
I think the focus should be less on a config file format and more on an API to get to those files. As a developer I might still want to be able to chose a format, say Not sure why you brought up CLI parsing. Although thinking about it now, I'm not sure how clap.rs handles windows arguments 😅 I haven't had a chance to play around with your crate yet but from the README it looks like it already exposes pretty much all the directory paths we might be interested in. At that point it becomes a question of making the API more ergonomic. i.e. maybe there could be a function to easily list configuration files for the given application (or |
I brought it up, sorry :) So, I've been thinking about what an all-around config solution might look like. We should not implement such a thing right now, but discuss what needs to happen to get there! Here's a small proposal that integrates ideas from clap (v3, this is future!) and configure to get the discussion going: #[derive(Debug, Deserialize, Clap, Configure)]
#[config(prefix = "diesel")]
struct Args {
#[clap(short = "q", long = "quiet")]
quiet: bool,
#[clap(long = "database-url")]
database_url: bool,
#[clap(subcommands)]
command: DieselCliCommand, // an enum defining subcommands with their own fields and attributes
}
fn main() {
let args = Args::configure()
.read_from(configure::adaptors::config_file::toml("diesel_cli.toml")) // Invokes serde
.read_from(configure::adaptors::env_file()) // dotenv
.read_from(configure::adaptors::env()) // std::env
.read_from(configure::adaptors::clap_auto_init()); // Clap incl. early exit on `-h` and stuff like that
} You can then:
Is that approximately the direction in which you want to go? What needs to happen to get there? |
I think the CLI/conf/env story should be in another issue. |
Sure, that was just for inspiration and to set some context. (If you have other use cases/ideas, please tell us :)) |
I have a couple of request in the structopt issues about that (no ideas, but persons wanting something like https://github.com/rust-lang-nursery/cli-wg/issues/7#issuecomment-367673115) |
Like @spacekookie said, I think it should focus on abstracting over platform specific issues and not on the format, or providing "key->value" style API. As the application writer, I want to just specify a file name, and let this crate handle where to store it. I then worry about formats, reading/writing, etc. Then later on someone could write a generic crate to abstract over this configure crate, using something like serde to give a key->value style API. Here's how I see the crate structure playing out (note, the crate names are just generic and not referring to anything existing right now). |
At a former employer, I wrote a config file management library (in Python) that turned out to be popular with my fellow developers (because it was easy to add to an existing project) and with our operations staff (because all our tools worked the same way, and the configuration was flexible enough for most of our use-cases). It worked like this:
Pros:
Cons:
If I were to attempt something similar in Rust:
|
One reason to consider a standard config file format, or at least a standard config data model: on Windows, perhaps the standard configuration source could/should be the Registry, rather than the filesystem? |
After some research on that, it seems that most developers recommend and prefer files over the registry: |
Since this thread is about the location of config files rather than their contents this may be a bit off topic, but here goes anyway: Similar to how structopt works, I'd love to do #[derive(Structconfig)]
pub struct Config {
timeout: u8
#[structconfig(name="retries", default=3)]
no_of_retries: u8,
files: Vec<PathBuf>,
} and have all the config stuff taken care of for me! Edit:
Didn't see that it had already been suggested. |
How did your code deal with first run, if there wasn't a config file? Did it assume you wanted to use the defaults, or did it exit and prompt you to create a config file? (or did it walk you through creating the config file interactively?) |
@derekdreery At first run, it would use the defaults. For the various tools we created, every config option always had a sensible out-of-the-box default. Things the program absolutely could not know without asking would generally be command-line arguments, not config options. It's a big world, and I'm sure there's some potential config options that cannot possibly have a sensible default, but I can't think of one right now. If anyone has an example, I'd love to hear it. |
Another aspect of config management to consider is passwords. Looks like there is a |
Hey everyone. I'm the current dev lead of conda, which is a cross-platform, system-level package manager. Currently written in python--but we're in the initial stages of considering transitioning key pieces to rust. Just wanted to add to this discussion how we do configuration, because it's been powerful and has worked out very well. It's also very similar to what @Screwtapello described. For each invocation of our executable, we build up a configuration context object from four sources of configuration information:
These are linearized in a way that the configuration sources conceptually closest to the process invocation take precedence. That is, if a configuration parameter is provided as a CLI flag, but also provided in a configuration file, the CLI-provided value would win. I guess the insight here is that most CLI applications deal with at least one configuration file, environment variables, and CLI flags anyway, and we've just realized that they all represent basically the same type of information, and can be generalized and unified. One capability that was especially important for us to add was the ability for sysadmins to lock down configuration for the entire system in "lower-level" read-only files. As we merge the sources of configuration information, we provide a flag sort of like the css I don't want to go into too much detail here. There's a blog post with more details, including how we deal with merging sequence and map-type configuration parameters. I did want to point all this out though as support for the usefulness of what @Screwtapello described. |
An alternative model that achieves the same goal is to have separate "config" and "override" files:
The advantage over an |
Just wanted to chime in with my two cents here as well, this actually caught me off guard recently with a tool I was using, If the default directories end up in I basically never assume configs or other data associated with my shell environment are going to end up anywhere other than in the XDG base directories I have set, or in the worst case, my home directory - but I would never even think to look in For tooling I build, I certainly will be overriding this behaviour in |
I closed dirs-dev/directories-rs#47, please have a look at the explanation in dirs-dev/directories-rs#47 (comment) for details. In this case, In general, if I had a buck for every time I heard "I shouldn't need to follow the platform's standards because of X" I could have retired multiple times already. Stuff like this is way we can't have nice things. |
@soc I understand that you do not want to include this in directories-rs, which probably also targets other (GUI) applications. However, as we have heard from multiple people in this thread (and multiple people in |
Also I'm not sure I understand the cache problem. Is it a big problem that cached files for CLI apps might be backed up? CLI caches do not tend to be that big in my experience. But even if it were a big problem, this is not a reason config files can't be placed in |
The difference between Linux and macOS is that Linux is a system written by developers, for developers. On the other hand., macOS is built by Apple, owned by Apple and controlled by Apple. The opinions of developers frankly doesn't matter on macOS – they are not calling the shots. If Apple ever decides to clamp down on applications not following their rules, I'm not going to take responsibility for the ensuing breakage. Just as a reminder,
is an excuse I heard more often than I care to count on Linux, too.
|
Pretty much every dependency manager on this planet is a CLI app. Have a look at the caches of Maven, npm or cargo. The cache directories are huge. |
But all of those store cache in the home dir on MacOS: More importantly, their config files are not buried somewhere deep in preferences but are all easily accessible in the home dir. |
Yes, I hope you now understand the issue with that (on both Linux and macOS). |
@soc
I do, however, think there is room for another crate that makes a different tradeoff here. |
Absolutely. That's why I said that I completely understand that @soc does not want to change |
Alternatively we could encourage developers to write applications which are well-behaved citizens of the platform they run on. In the beginning of medicine people also thought there was room for not washing hands before a procedure. Thankfully, washing hands became non-optional, even though many considered it to be surprising and unexpected at that time. I'm optimistic that – with an effort to educate developers – a similar success story can be achieved here. |
Counterpoint: the reason we disagree isn't because we need to be "educated" that we're "share-croppers" who need to learn to be "well-behaved", but because we view the platform differently. Many use macOS because it's the best way to get a unix with a good gui. Remember, with the command line on Mac we're talking about an open-source unix that effectively shares a userspace with Linux. That shared GNU/Linux/POSIX userspace is what we think of when we use the command line on Mac. It's why we'd prefer to share system conventions with it when it makes sense to. |
This topic isn't being very productive, and I don't think it should be discussed further on this issue. We'd be better served by working towards one of other the aspects of configuration file management we've yet to address, rather than circling around a difference in opinion. |
While this may not be satisfactory for a number of reasons, I'd like to say that ripgrep's approach to configuration files has generally side-stepped all of the issues mentioned here, and generally has received zero complaints from users. The trick is to opt out of encoding knowledge about xdg (or similar) at all in the first place. Namely, ripgrep requires users to set an environment variable that points to the location of the config file. That location can be anywhere. The user can choose any arbitrary location. People like me who gave up caring about the cleanliness of their home directory can plop it at $HOME/.ripgreprc, while macOS users rebelling against their overlords can drop it into an xdg compatible location if it suits them. As I said initially, this is not satisfactory in all cases for a number of reasons. One such example is that this really only applies to configuration. If your application needs to make use of cache directories, for example, then forcing the user to set an environment variable is probably bad UX and not something they'd expect. My approach also only works if the configuration file is entirely optional (which is honestly probably a generally good idea anyway). Anywho, I don't mean to propose this as a panacea, but it's something that can work well in a specific circumstance for maintainers that don't want to be on the wrong side of a horde of users because your program put the config file in the wrong place. |
Regarding layered configs: I've been playing with this with Aspects of
To get an idea of how this works, see And some solution exploration I feel like a toolbox approach to support layered config would be more useful than an all-in-one / opinionated solutions. For example
|
I just published my first rust library on crates.io which is a config library that I plan to use in another application I'm developing. It's currently lacking quite a bit in documentation (but there is some), but it provides configuration (either configured by the application, or by convention) from multiple places (user folders, application folders, environment variables and cli arguments). Binding to structs is still not available though (but should be reasonably straight forward). Sample usage looks like this: #[derive(ClapConfig)]
pub struct Server {
#[preftool_clap(help = "Server host")]
host: String,
#[preftool_clap(help = "Server port")]
port: usize,
#[preftool_clap(help = "Server certificate (enables TLS)")]
cert: Option<String>,
}
fn main() -> std::io::Result<()> {
let mut builder = TerminalLoggerBuilder::new();
builder.level(Severity::Debug);
builder.destination(Destination::Stdout);
let log = builder.build().unwrap();
let config = AppConfigBuilder::new("preftool-app-config-simple", Some(log))?
.with_args::<Server>()
.configure(|app| app.version("0.0.1"))
.add_format(TomlConfigFormat)
.build()?;
println!("{:#?}", config);
Ok(())
} This will read config from toml files, environment variables, and application arguments. Code is available here: https://github.com/YoloDev/preftool. |
@Alxandr Very interesting library! One note, please do not use |
@soc issue is that neither one of those two provides me the information that I need though. I will probably have to go back to just having the functions embedded instead. I switched to |
Feel free to use it at your own risk. I just wanted to make sure that the level of non-guarantees has been communicated. :-) |
Fair enough. I would love a stable API with the low-level primitives I though ;-). |
I just made/published platform-dirs which provides a simple API for retrieving platform dependent application directories and user directories. It also allows for specifying if an application is CLI based and uses the XDG specified application directories on macOS if so. There's some other differences relative to dirs-rs and directories-rs at the bottom of the readme too. It's still beta-level so the API may change and it may have bugs FYI. Any and all suggestions welcome! |
From Discord mike7c2
|
Reformatted for your reading pleasure:
|
(moderated summary by WG)
Context
Plan of Action
In-ecosystem resources
External inspiration
Challenges
@spacekookie's original poist
In the first meeting, we discussed the task of file management on different platforms (particularly configurations) and how it can be made better.
@Eijebong summarised it like this
There is a crate for "determing system configuration" (app-dirs-rs) but it seems unmaintained and not up to date
The text was updated successfully, but these errors were encountered: