Skip to content
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

Initial draft of sdk unified config file doc #328

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft

Conversation

baronfel
Copy link
Member

@baronfel baronfel commented Jan 29, 2025

We have about 15 (/s) different configuration files and formats that rev independently by different teams. Users would be well-served by a single source of truth that all tools can read from, that has unified documentation and comes accompanied by helper libraries to ensure consistency of behavior. The first step along that journey is having a shared understanding of the config system and its intended uses, so that we can build tooling on top of that shared understanding.


## What exists today

Today there are several places where teams have put configuration for repo- or project-level configuration for tooling:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May or may not be relevant, but I'd also consider Directory.Build.rsp as another of these types of configuration sources.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, we'll need guidance for when to put something here versus Directory.Build.props. For instance, where should I put TreatWarningsAsErrors? I think that has a surprisingly big impact for MSBuild, given that one's a default property and one's a global property.


### Nested keys

We should encourage tools to stake a claim on a top-level 'namespace' for keys in this configuration file. Keys should either be namespaced with the tool's name or be namespaced with a common prefix that is unique to the tool. This will prevent key collisions between tools and make it easier for users to understand what each key does. This allows for the format to be open to extensibility, as new tools can add their own keys without fear of collision.
Copy link
Member

@MattKotsenas MattKotsenas Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider from the outset some type of namespace or "vendor" prefix, even if it's just guidance without code enforcement. I get the point of not wanting to appear to privilege our own tools over 3rd parties, however I'm also sensitive to the risk of picking suboptimal names to avoid backwards incompatibility or collisions at the top-level.

We must ensure that both the CLI and VS support the configuration file and the configuration system.

#### Usage within the dotnet CLI and supporting tools
Inside the `dotnet` CLI (and tools that ship within the CLI), we will initialize a `ConfigurationBuilder` with the `poohbear.config` file and merge it with the user's `poohbear.config` file. We will also seed that builder with environment variables. Tools that need the configuration within the `dotnet` CLI will read the appropriate values and apply them to their execution.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the tool / format starts to support env vars or other sources, consider adding a dotnet config dump or dotnet config get --all command or similar so that folks can debug setup issues.

The [git config][git-config] command is a good source of inspiration.


#### Programmatic access
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A different type of programmatic access, but should this file be available to Roslyn as well? For instance, should it be a goal, non-goal, etc. to allow something like stylecop.json or BannedSymbols.txt to be here instead?


### Composition and heirarchy

This file will be hierarchical in a very limited fashion. The final realized configuration will be the result of merging the `poohbear.config` file in the repository root with an optional `poohbear.config` file in the user's `$HOME` directory (specifically, the SpecialFolders.UserProfile location in the .NET BCL). This will allow users to set defaults for their own use, and then override those defaults on a per-repository basis for sharing with coworkers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat silly question for me, but how do you know what the repository root is? Are you searching up for the first you find? What if they nest?

Possible options:

  • Allow nesting similar to .editorconfig, optionally with an is_root style property
  • Find the first; if so, what if the first found isn't in $HOME but is "above" the .git folder?
  • Something else?

git config doesn't have these problems because you never actually interact with the file; here it's visible to the user (which is good) but that means you can and will end up with every combination possible :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As ubiquitous as Git is these days surely there are still customers that don't use Git. Why wouldn't the hierarchy follow the similar pattern of global.json and look for a file up the parent directory till you find a file or reach the root. And of course take into account the $HOME directory.

@baronfel
Copy link
Member Author

baronfel commented Jan 31, 2025

Feedback from @KathleenDollard:

  • have a story in here about how to version defaults for this config
  • have a place for 'future work' and explicitly aim at global.json here too (the function, maybe not the form of version + roll forward configuration)

* unified opt-out of telemetry
* storing tooling decisions like which mode `dotnet test` should execute in

All kinds of repository-wide configuration need a unified place to be expressed that we lack today.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to extend global.json instead of introducing yet another file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth having the conversation, but global.json first and foremost belongs to the runtime today. I'd like to have something that tooling can work with that has guaranteed zero impact on runtime scenarios.

There is a bit of https://xkcd.com/927/ in this proposal, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global.json first and foremost belongs to the runtime today

From my point of view, global.json belongs to the SDK. There is a tiny bit of code in in dotnet/runtime that reads it to find the version of SDK to call. This code only ever runs when dotnet is used to invoke SDK.

Also, different parts of SDK or SDK extensions have started to use global.json to store random configuration. Check what's in global.json in the dotnet/sdk repo: https://github.com/dotnet/sdk/blob/main/global.json


The choice of format is in some ways limited by engineering considerations. The easiest thing would be to choose a format that is already supported by the BCL, which would lead us to JSON, XML, or INI. JSON and XML formats have downsides, though: JSON doesn't support comments and has an anemic data model, and XML is historically verbose and difficult to parse in a consistent way.

The recent wave of programming languages and programming-related tools have popularized the use of more human-readable formats like YAML, TOML, and INI. These formats are more human-readable and have more expressive power than JSON or XML. None of them have parsers in the BCL, though, which would mean that we would have to write our own parser or use a third-party library. Writing a parser is a potentially error-prone task, and using a third-party library would require onboarding the parser to source build as well as doing a thorough security audit. YAML specifically has such a lax parser that it might be a security risk to use it.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add KDL

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest KDL, it's so good

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow uhhh yeah that would be awesome.

fwiw, KDL might be standardizing officially with Ecma going forward. It would also be VERY easy to map msbuild concepts to it (see this translated csproj example).

It’s worth noting that KDL was created specifically because I ended up not wanting to use TOML as a configuration file. Some reasons being practical (like the difficulty with nesting and how strict the formatting needs are), and others being ideological (I just do not want to touch things so closely associated with that author, a sentiment many in my circles share).

Going from there, KDL was very carefully designed to bring in a lot of the practicality that people want from YAML with much more safety. In all but a few rare cases with specific Unicode shenanigans (for example, certain invisible spaces that Unicode specifically requires not be treated as spaces), a KDL document will be exactly what it seems to be to a human reader with any given editor.

Anyway I’m more than happy to answer questions about it, its tooling and adoption status, impressions from existing tools that use it for configuration, and even do some porting of example configs to show off potential benefits of KDL-specific features. You can also reach me internally, although I’m OOF until mid-March right now.

@baronfel
Copy link
Member Author

baronfel commented Feb 3, 2025

Add risks for parsers for non-BCL-included formats.
Add risk if we can't replace global.json (leaving one extra file).

@nohwnd
Copy link
Member

nohwnd commented Feb 4, 2025

For the tooling, I would love if there was a supported and standard way of preserving this configuration into the finished artifacts, and merging the configurations from multiple artifacts into one. This way the configuration is not just for the build-time, but also for execution.

Lot of this is probably already covered by the config subsystem, but here are the the usecases I have in mind:

  • User defines their default test run preferences using poohbear.config. They build / publish their solution. Then they move their bin folder to 3 different agents to run tests on windows, macos, linux. These agents don't have dotnet sdk.

    On the target system they run the app via dotnet exec. Their configuration should be the same as if they used dotnet test on the original system, and not require additional copy of poohbear.config from the root and all applicable poohbear.config files.

  • User does the above, but they have 10 and not 1 dll to run. A test run orchestrator (that does not require dotnet sdk), should be able to collect configuration from all sources, and merge them to get the correct "solution" configuration, and correct per-project configuration. ( definition of which setting applies for whole "solution" and which one applies per project is up to us, but preferably there should be an easy way to read configurations, and merge them).

  • and similar for nativeaot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants