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

Config file: support for project level options #9188

Open
stsewd opened this issue May 13, 2022 · 11 comments
Open

Config file: support for project level options #9188

stsewd opened this issue May 13, 2022 · 11 comments
Labels
Needed: design decision A core team decision is required

Comments

@stsewd
Copy link
Member

stsewd commented May 13, 2022

Discussions

Interesting readings

Read the Docs

Currently we have:

  • Project level options in the UI
  • Version/build level options in the config file (except for a couple)
  • We have a default branch (usually the same default branch from the repo, main/master),
    and a default version (where / redirects to).

Current ideas:

  • Two config files, one with the usual settings,
    and another one with the global settings.

    • Having another config file could be confusing for users.
    • Will add complexity to our code base:
      parse and keep track of another file, check compatibility between the version of one file and the other.
    • It makes it clear what options are global and which aren't.
  • One config file, with global options under a specific key like on_default, when_default.

    • It makes it clear what options are global and which aren't.
    • Another nested option.
    • Could imply that the we have a global and per-version version of a setting
      (like redirects). This is bad or good depending on how we want to implement it.
  • One config file, with some options that are global only and the normal ones.

    • Isn't clear what options are global or local,
      users will need to read the docs.
    • No major disruption from the current config file,
      they are just another options.

Local vs global options

I think implicitly we have distinguished them
by the scope they are applied to and their presence
in the UI.

We could use another rule to classify them:

  • If It needs to re-build all versions to take effect, it is local.
  • If it doesn't need to re-build all versions to take effect, it is global.

I think most of our current options in the config file
are there because they are easy to classify as local options,
since they are very specific to the build process.

There are a couple of options that we could classify as options with "preview" support,
this is, options the we can see their effects before they are applied
to the project itself (redirects and PR build options).

New options

Let's see how the new proposed options could be applied:

PR build options

This doesn't change how a version is build,
and it doesn't have a "version" where it's applied.
So, it is a global option, but we may also want to see it in action in the current build,
like build the PR, but not report the status,
always report the status as green, etc.

This could be a global option with preview support (I think this should be easy to support,
just passing the options around).

Environment variables

They can be applied to the current build only.
We could also make them global,
merging them with the ones from the UI,
but it could be confusing when we build
another version, we will need to merge the variables
from the UI, then the ones from the default branch,
and at last, the ones from the current version.

The ones from the UI are still useful, to enter secrets,
and to apply them to all builds.

This could be per-version option,
and are merged with the ones from the UI (config file env vars have precedence)

Redirects

They are applied to the whole project.
we could scope them to be per-version,
but I think that could introduce some confusion
and users will need to re-release a version if they want to change
a redirect from an old version,
it also makes it hard to see what are all the redirects that the project has,
you'll need to check all versions and merge them together,
it also brings up questions like: do they override the global ones or
add a new one? And our current implementation will need some changes
to track the version.

This could be a global option with preview support (this could be implemented later),
the redirects from the config file are added to the ones from the UI,
they won't replace existing ones.

Automation rules

They are applied to the whole project,
we could scope them to be per-version,
but as redirects it could also be confusing
and hard to track/change.

This could be a global option (the options will need to be on the default branch to take effect),
the rules from the config file are added to the ones from the UI, they won't replace existing ones.

Search

We already have this one as a local option.
I think we are okay with that,
as we need to re-build the version in order to take effect.

Conclusion and design decisions

  • Read the global options from the default branch.
  • I think we are okay with one file,
    personally I think we are okay without a key to scope the global options,
    it's easy to see what options are global.
  • Options from the default branch will be stored in our DB when the build finish (successfully),
    but won't be accessible from the UI.
  • We need a new field on the global options to track the ones from the UI and from the
    config file, that way users won't be able to change those from the UI and
    we will still have them in our DB.
  • Options from the config file are added to the ones from the UI (except for env vars),
    and they have priority over the ones from the UI (the other way around also works for me).
  • We don't have a global and per-version version of an option,
    but we merge/add them together and are applied to the whole project (redirects, automation rules)
    or the current build (env vars).
  • We could also remove those options from the UI and make it required
    to use the config file only (I'm not sure that I like much this idea, the UI is more friendly
    for non-technical users and more easy to discover).
@humitos
Copy link
Member

humitos commented May 16, 2022

I think this is a good summary of the current state and how other services are solving this problem. Besides, it points to multiple places where we already have a similar conversation 👍🏼

However, I read the issue multiple times and it's still not clear to me what are the main goals we want to achieve: should delete the project and re-import it keep it building as it was and work with zero-config or would the user need to add/change some settings from the UI? Do we want to support monorepos with this change as well? Why not follow the approach that Netlify is using? What would be our limitations in that case? (it supports redirects, environment variables, monorepos, and other features we have but we do not support on the config file)

Also, I think we've discussed good ideas in other issues, in particular this comment #8811 (comment), but they are not mentioned here with their pros/cons (note that when setting up a project on CircleCI, it asks you for the branch where you want to read the config file --which is something we talked on that issue: point 6. in this section https://circleci.com/docs/2.0/config-intro/#part-1-using-the-shell). I'm not sure if it makes sense to recover those conversations and keep discussing them. I suppose that @agjohnson will have more opinions on this.


  • If It needs to re-build all versions to take effect, it is local.
  • If it doesn't need to re-build all versions to take effect, it is global.

I'm a little confused with this. Shouldn't this be inverted? If it needs to re-build all the versions, it's global, right?

Also, I assume that local is per-version and global is per-project config, right?

I think we are okay with one file, personally I think we are okay without a key to scope the global options, it's easy to see what options are global.

I'm not following why it's easy to see what options are global without a key to scope them? How the user would know that environment key is global/local without reading the documentation?

We don't have a global and per-version version of an option but we merge/add them together and are applied to the whole project (redirects, automation rules) or the current build (env vars).

I don't understand this. Would it be possible to have redirects key in the default branch but also in the particular branch? In that case, they will be merged? Which one will have priority over the same from_url?

We could also remove those options from the UI and make it required to use the config file only (I'm not sure that I like much this idea, the UI is more friendly for non-technical users and more easy to discover).

Would it be easier to reduce complexity and avoid merging UI with config file settings completely? It seems that we have a lot of technical complexity because of this but also seems hard to communicate to users correctly generating some confusion about which options can be modified from the UI and which ones take precedence over the others. Wouldn't be good to use version: 3 in the config file and in that case disable the UI completely? Then all the settings need to be in the config file when using version: 3 in the config file from the default branch. That will make us move in the direction we want, reducing users interacting with the settings page and making projects 100% re-importable without manual intervention.

@ericholscher
Copy link
Member

ericholscher commented May 16, 2022

I already feel like this conversation would benefit from a meeting. Let's pencil it in for Wednesday?

Read the global options from the default branch.

Agree here. I don't think we have a better option, and we've already done this a few different times in other cases for globalish state.

Options from the default branch will be stored in our DB when the build finish (successfully),
but won't be accessible from the UI.

This is interesting. I'd love to be able to disable the web-based UI for elements that we know they are overriding in their config file. I feel like we're going to end up with 3 places for configuration:

  • Web-based UI
  • Default branch global config
  • Version-specific config

This is going to be pretty confusing for users, but it seems like we could ease this by making it really obvious in the web UI if the option will be overridden by the global config.

From @humitos: However, I read the issue multiple times and it's still not clear to me what are the main goals we want to achieve: should delete the project and re-import it keep it building as it was and work with zero-config or would the user need to add/change some settings from the UI? Do we want to support monorepos with this change as well? Why not follow the approach that Netlify is using? What would be our limitations in that case? (it supports redirects, environment variables, monorepos, and other features we have but we do not support on the config file)

I don't see how the Netlify option works for us? We already have a config file, the problem we're trying to solve is how that config file contains global state. It seems Netlify doesn't solve this problem, and just only has global state since they only deploy a single branch. Using the default branch as the default configuration seems like the same approach.

I believe what Santos is proposing is similar. Basically there's a config file that overrides the web UI options, and has a default branch that contains global settings. Netlify specifies per-version settings in the global config under deploy preview settings.

@humitos is that what you're proposing we do, having no version-specific configuration files, and only a global?

Would it be easier to reduce complexity and avoid merging UI with config file settings completely? It seems that we have a lot of technical complexity because of this but also seems hard to communicate to users correctly generating some confusion about which options can be modified from the UI and which ones take precedence over the others. Wouldn't be good to use version: 3 in the config file and in that case disable the UI completely? Then all the settings need to be in the config file when using version: 3 in the config file from the default branch. That will make us move in the direction we want, reducing users interacting with the settings page and making projects 100% re-importable without manual intervention.

I agree with this. I'd love to be able to stop merging these values. We would probably want to refactor the "version-specific" settings in the web UI and just disable that entire page for a project that has a version 3 config?

@stsewd
Copy link
Member Author

stsewd commented May 16, 2022

Also, I think we've discussed good ideas in other issues, in particular this comment #8811 (comment), but they are not mentioned here with their pros/cons

That issue is specific to introduce support for monorepos with multiple config files per project, this issue is specific to support project level options in the config file.

should delete the project and re-import it keep it building as it was and work with zero-config or would the user need to add/change some settings from the UI?

That would depend on the owner of the project, but I think we should push users to put everything on the config file, but this isn't possible for private env vars for example.

I'm a little confused with this. Shouldn't this be inverted? If it needs to re-build all the versions, it's global, right?

A global option would affect the whole project, and that option would be read from one location (default branch, UI). For example, search options take effect in the build itself, if you want to change the search options on all versions, you'll need to push that change and re-build each version (local option), and an example for a global option is redirects, you need to push your changes on the default branch only, but it doesn't require building each version in order to take effect.

I'm not following why it's easy to see what options are global without a key to scope them? How the user would know that environment key is global/local without reading the documentation?

The environment variables will be applied per build, this is similar to other CI services (if you re-build another version it will take the env vars from the current commit, not from the default branch). Other options like redirects aren't attached to a build, so they are global.

I don't understand this. Would it be possible to have redirects key in the default branch but also in the particular branch? In that case, they will be merged?

I mentioned in the issue the possibility of having per-version redirects, and concluded that they will be confusing and hard to manage, so there aren't per-version options really, there are global options in the UI that are merged with the global options from the default branch and there are per-build options that we don't need to keep track of (these options affect the build itself, nothing else).

Which one will have priority over the same from_url?

This would be the same as we have today, you can create several redirects that match the from_url part, we use the update date for priority

ordering = ('-update_dt',)

This is interesting. I'd love to be able to disable the web-based UI for elements that we know they are overriding in their config file. I feel like we're going to end up with 3 places for configuration:

There would be only two places, UI and default branch, the "specific" version config isn't something we would support (like redirects per version), what you probably are referring to is build specific settings, and those we don't need to store in our DB, since they only affect the build itself (like search).

Would it be easier to reduce complexity and avoid merging UI with config file settings completely?

That would be an interesting take, but we can't do that with all options, environment variables should be merged with the ones from the UI since they would contain secrets.


A little more clarification about my proposal, we don't have per-version options, what we have are options that affect the current build only (like search, python version, etc), a per-version option would be something like redirects that affect only that version (on the issue I have discarded this being a thing, because it would be confusing). What we would have is:

  • Global options from the UI (no per-build options like path to the conf.py file, python version, etc, but things like redirects, env vars, automation rules). Env vars are in a gray area here since they do affect the build itself (but we need a place to put secrets outside the repo, so)

  • Global options from the default branch, these will be added with the ones from the UI, and they don't affect the build itself (like redirects, automation rules, etc)

    • A subcategory here are the options that support "preview" (like redirects, or PR options), but this is just an extra idea.
  • Local options from the config file, these are the options that affect the build itself, we don't need to keep track of these after the build has ended, well search is kind of in a gray area here I guess. Examples are env vars, python version, sphinx config file, etc.

@humitos
Copy link
Member

humitos commented May 17, 2022

I think this sounds good in general. There are some details that I'd like to work on more to make this proposal better going forward:

  • I'd like to disable the UI completely when using the new config file with global options. I don't like the idea of merging settings from both places. However, I'd keep the tab for environment variables that contain secrets (and maybe other requires we could have in the future), since it's required.
  • I like default_branch being the configs that take preference over the others 👍🏼
  • It seems we should find a simple/clean way to force the invert of this preference sometimes when building from pull requests. Otherwise, users won't be sure the pull requests will work after merging it (e.g. changing the to_url of a redirect or an environment variable). Basically, "from pull requests we need to be able to modify global configs to preview them" --which sounds weird while I'm writing it down, tho
  • Do we require the default_branch to be "buildable" and have to succeed to apply those configs?
  • IMO, order of redirects shouldn't be based on updated_dt at all. I'd like to get rid of that. It's super confusing and we are not exposing that anywhere.

Would some use cases and examples of the .readthedocs.yaml files on different versions help to be all of us on the same page and analyze those use cases/examples? Maybe this is useful to know if the proposal solves those use cases and if we are on the right path.

@ericholscher

I already feel like this conversation would benefit from a meeting. Let's pencil it in for Wednesday?

Sounds good to me, 👍🏼 . I sent an invite already.

@humitos
Copy link
Member

humitos commented May 17, 2022

@stsewd

PR build options

I like the idea we talked about in #6046 (comment) to use automation rules for this. Now, with this proposal, if we can write automation rules in the config file, we could use them to decide whether pull requests should build or not with a matching pattern over the branch name 💯

@agjohnson
Copy link
Contributor

Thanks for the great summary @stsewd!

I already feel like this conversation would benefit from a meeting. Let's pencil it in for Wednesday?

I'd also benefit from structuring the conversation a bit. I'd like to focus on a few questions and put a cap on scope of this work.

I'm definitely happy to spend time brainstorming details. This work directly affects how we support multiple versions in a project, and so it's something we should really put some care into implementing.

However, I read the issue multiple times and it's still not clear to me what are the main goals we want to achieve

This is my main concern. Immediately, we need patterns to support some project level settings in our configuration file -- redirects being one, project level search config being another. Removing UI for project level options is a separate goal, one that mostly helps core team perhaps.

I think we can work towards removing some settings from our UI. But to eliminate UI with this change is really expanding scope of this project to something much less digestible.

Advanced settings default settings would be good settings to plan to eliminate eventually though. I'm less convinced it's worth our time to remove the other project level settings.

Do we want to support monorepos with this change as well?

That issue is specific to introduce support for monorepos with multiple config files per project, this issue is specific to support project level options in the config file.

I had this question as well. The two are a singular issue I feel. We don't need to figure out specifically what monorepo support is just yet. But how we support per-project options needs to play well with monorepo support. I'm not sure that's something we can do as well in isolation, and it makes sense to avoid competing implementations.

A little more clarification about my proposal, we don't have per-version options

I sort of agree with this, but am finding it's hard to talk about the same things if we change terminology too. Even if a configuration option only affects a single build, that single build's output is what we refer to as a "version". So for that, build-specific options are also version-specific configuration options.

@stsewd Are there any options that are explicitly version-specific or build-specific?

I mentioned in the issue the possibility of having per-version redirects, and concluded that they will be confusing and hard to manage

I'm not not -1 on this, but feel we'd need some advanced features in redirects to make this UX nice for users if we were to go this route.

There are a couple points where version-level redirects really nail the UX much better. Duplicating redirects across versions is one. But the major one is the ability to be explicit about which redirects are active on a version. Maybe in PR review, but definitely in merging to a branch, the version-specific redirects I defined in that branch's .readthedocs.yaml are active on merge.

Instead, with project-level redirects, I have to go back to my main branch and futz with the redirects to make them active on a branch, do another PR, etc.

As a user of CI services, commit-specific configuration is a fantastic feature. And as a user of RTD, version-specific redirects would similarly feel like first class support of versions.

Would it be easier to reduce complexity and avoid merging UI with config file settings completely?

@humitos can you elaborate on the settings you're talking about removing? Perhaps I'm interpreting this as a lot more work than what you're describing.

While I agree there is some configuration we can move to the configuration file, I've never been convinced that we should move everything to our configuration file. Project-specific configuration is already going to be awkward UX, so I'm concerned we're making worse UX for users.

Mostly what I worry about is projects will now have project-level configuration defined in all of their branches' .readthedocs.yaml, but that configuration is only used from the default branch. I fully expect to have to field support questions about this.

To me, removing UI sounds like a secondary project, and I'm not really convinced it brings value to users -- perhaps even makes our product harder to use depending on what the scope of UI removal we're actually talking about here.


To summarize where I'm at after #8811

  • Agree that using the default branch for project-specific options is the best idea we've had so far
  • I think we need a namespace (or similar) for project-specific options though. If users can't easily determine which option is version-specific and which are project-specific, they will all look version-specific because .readthedocs.yaml is version-specific.
  • Agree we only want one file, not multiple config files
  • I think we still need version-specific redirects for nice UX

@agjohnson
Copy link
Contributor

As to what it seems we need to discuss more tomorrow:

  • Define the goal and scope of this work explicitly
  • Agree on single file approach?
    • Single .readthedocs.yaml, not multiple configuration files
    • Default branch dictates project-specific configuration
    • Each version still has version-specific configuration
  • Agree on saving project-level configuration to database?
  • Agree on namespace for project-level configuration?
  • What is the scope of fields we're adding to the configuration file?
  • What scope of disabling the UI are we discussing?
  • What exactly is the UX when disabling form fields?
    • How are we making this accessible to non-technical users?

More?

@stsewd
Copy link
Member Author

stsewd commented May 17, 2022

It seems we should find a simple/clean way to force the invert of this preference sometimes when building from pull requests.

that's what I'm describing as global options with "preview" support, they will have effect only on the docs from that PR.

Do we require the default_branch to be "buildable" and have to succeed to apply those configs?

I'm +1 on making it required for the build to pass in order to apply the settings

IMO, order of redirects shouldn't be based on updated_dt at all. I'd like to get rid of that. It's super confusing and we are not exposing that anywhere.

We could use the same behavior from automation rules (having an explicit order that users can change), in the config file should be easier just follow the order they are written.

I sort of agree with this, but am finding it's hard to talk about the same things if we change terminology too.

yeah, just making it clear what we mean for per-version settings in this context of this proposal.

Are there any options that are explicitly version-specific or build-specific?

Build specific are all the current ones from the config file. Env vars will be build-specfic too (but merged with the ones from the admin)

As a user of CI services, commit-specific configuration is a fantastic feature. And as a user of RTD, version-specific redirects would similarly feel like first class support of versions.

the problems users will find with version specific redirects are:

  • users will need to re-release a version if they want to change a redirect from an old version
  • It makes it hard to see what are all the redirects that the project has, you'll need to check all versions and merge them together.
  • It brings questions like: do they override the global ones, or they add a new one?

@stsewd
Copy link
Member Author

stsewd commented May 18, 2022

oh, and if you can think of another service similar to us that have a config file that would be great, so far I wasn't able to think of a service that maps directly to what we do, but anything closer is great.

@agjohnson
Copy link
Contributor

Heh yeah, that's the trouble. We haven't come across any good prior art combining per-branch and per-repo configuration. It's fairly specific to our service, and indeed is already feeling like rather strange UX.

The middle ground is to only extends this UX to redirects and search, giving us time to figure out some of these patterns with user feedback. It's probably wise to scope this project to just those for now, given we don't feel we have an obvious path forward.

@ericholscher
Copy link
Member

Takeaways:

  • Seems like we don't have a pattern for project-specific config that isn't very confusing to users. We're thinking it doesn't make sense, and we should move forward with DB-based settings
    • Users can work around this limitation by having a CI set the DB settings from some data in their repo, which is more flexible. Perhaps use terraform or readthedocs-cli, as examples of users already doing this without us supporting it.
    • There's also some content design work to put settings into nicer buckets in our DB UI, if we're going to keep it.
  • We want to redesign redirects to have a nicer mental model, where project-level & version-level are easy to understand. Maybe just only project-level redirects, but scoped to a regex of versions somehow?
    • We also want the ability to force a redirect on 200, as an option.
  • How do we handle reapplying project-level variables across all versions? Are there things we'll want to override (eg. env vars), but then also have a global option to override the override (eg. for tags where the code can't change, or a user with lots of versions)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needed: design decision A core team decision is required
Projects
None yet
Development

No branches or pull requests

4 participants