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
refactor: Use project_list.yaml as source of truth, fixes #5918 #5926
refactor: Use project_list.yaml as source of truth, fixes #5918 #5926
Conversation
Download the artifacts for this pull request:
See Testing a PR. |
pkg/globalconfig/global_config.go
Outdated
// Remove empty project list from global config | ||
// In a future release it will be removed from the struct but | ||
// for now it's needed to make upgrading easier. | ||
cfgbytes = []byte(strings.Replace(string(cfgbytes), "project_info: {}", "", 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to ensure we don't have an empty project list in global config. It felt quite hacky, but was better than keeping an empty list in the file which would lead to further confusion.
pkg/globalconfig/global_config.go
Outdated
ProjectList map[string]*ProjectInfo `yaml:"project_info"` | ||
// ProjectList is retained to make upgrading easier and should be removed after a few releases | ||
ProjectList map[string]*ProjectInfo `yaml:"project_info"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't like keeping this in the type since it's no longer intended to be kept there, but this is how we'd do this if we don't have an intermediary legacy type.
pkg/globalconfig/global_config.go
Outdated
// Old versions of DDEV stored the project list in the global config. | ||
// For ease of upgrade, we need to be able to extract that data and move it to | ||
// the new project_list.yaml | ||
// In a future release, remove this. | ||
type LegacyGlobalConfig struct { | ||
ProjectList map[string]*ProjectInfo `yaml:"project_info"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lets us remove ProjectList
from the GlobalConfig
type. It means any time the global config is saved, the project list (if there was one in the file) is removed without having to do something hackish like an above comment points out.
It also gives us a clean way to marshal the list from the YAML file - if we removed ProjectList
from GlobalConfig
but didn't add something like this, we'd have to parse the file manually which would be even worse.
pkg/globalconfig/global_config.go
Outdated
// Get the legacy project list if there is one | ||
err = yaml.Unmarshal(source, &ddevLegacyGlobalConfig) | ||
if err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do this in ReadGlobalConfig
because this is the best point to guarantee that:
- We haven't overwritten the config file yet
- We haven't run
ReadProjectList
yet
pkg/globalconfig/global_config.go
Outdated
// If someone upgrades from an earlier version, the global config may hold the project list. | ||
if ddevLegacyGlobalConfig.ProjectList != nil && len(ddevLegacyGlobalConfig.ProjectList) > 0 { | ||
DdevProjectList = ddevLegacyGlobalConfig.ProjectList | ||
err := WriteProjectList(DdevProjectList) | ||
// Whether there's an error or nil here we want to return | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only want to check legacy config if the file doesn't exist. If the file exists, it's the source of truth even if it's empty.
// Whether there's an error or nil here we want to return | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We either created a new file here, or we had an unexpected error. Either way there's no projects to read so we can skip the "read the projects list from the file" logic below.
Moved to draft until tests start working |
This is essential for DDEV v1.23.0 IMO, thanks for all your work on it, and we know you don't have unlimited time for it. |
d3676e3
to
ac05207
Compare
Rebased |
ac05207
to
7e3f55e
Compare
Rebased again 'cause GitHub said it was still out of date with Master. The error can be easily reproduced by running GOTEST_SHORT=1 make test TESTARGS="-run TestConfigDatabaseVersion"
If we keep this part of the code from if !reflect.DeepEqual(DdevGlobalConfig.ProjectList, DdevProjectList) {
DdevProjectList = DdevGlobalConfig.ProjectList
err := WriteProjectList(DdevProjectList)
if err != nil {
return err
}
} In other words, something in the tests is either writing directly to the project list in global config, or writing the wrong thing directly to the new project list. The two get out of sync, so it grabs everything from the global config and discards whatever's in the new list. Or maybe that's a red herring, I'm really not sure. I think the problem might lie in |
I tried adding err = globalconfig.RemoveProjectInfo(t.Name())
assert.NoError(err) into the Appending |
Changing the name of the first project that's created in the test changes the name of the project that is included in the error message. So it's clearly explicitly complaining that that first-created project exists and points to the same directory that the I just for the life of me don't seem to be able to find a way to remove that project correctly before creating the next one. |
7e3f55e
to
e34353a
Compare
Okay, I have something that passes that test locally now. I'm pretty sure the change I made to the test is okay, but please do check that the test is still testing what it needs to test. |
|
I re-ran the nginx-fpm test. It's always good to understand what's going on with a fail, but you're always welcome to restart. |
I only have the ability to restart the buildkite builds - I don't seem to have the right permissions for restart gha ones. |
Sent you invitation. Hopefully that will give you the power. |
Aha! Yes, thank you, I have the option now for next time 💙 |
All green! 🥳 |
Great work @GuySartorelli! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure this is fine, and will run through a quick test, but would love to discuss/consider just doing
ProjectList map[string]*ProjectInfo `yaml:"project_info,omitempty"`
and make sure that it gets emptied after processing.
cmd/ddev/cmd/config_test.go
Outdated
@@ -514,6 +514,8 @@ func TestConfigDatabaseVersion(t *testing.T) { | |||
// add a database into the config and nothing else | |||
for _, dbTypeVersion := range versionsToTest { | |||
_ = app.Stop(true, false) | |||
_, err := exec.RunHostCommand(DdevBin, "delete", "-Oy", t.Name()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how this does any harm, but what did you hit here that made you do this? Is it related to this PR? Did some old project get left laying around?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #5926 (comment) and all my comments in this PR since then :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please revisit this without the delete here to see if you can find out what the real problem is? It appears that you're having to patch something that is caused by something entirely different. This kind of adaptation of a test is really suspicious, as you know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks to me like this code is just trying to call ddev config
inside a directory which already has a ddev project created and that this should have been failing previously
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except... it doesn't seem to have failed except in this PR. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. The bit of code I mentioned in #5926 (comment) is being removed in this PR - if we keep that there we don't get the error, because there's no projects in the global config list and there are projects in the new projects list. That discrepancy combined with the mentioned code results in clearing the new project list, so as far as DDEV is concerned there's no project there.
This PR removes that code, which means DDEV is now fully aware that there is a project there, causing the error.
I'm not sure if this was something introduced when I added the new project list file, or since then, or maybe I'm just wrong about the cause here? But that's what it looks like to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for chasing it. I'm often unable to follow your references, too many context switches. If you could re-mention things instead of linking to other comments maybe I'll do better. It sounds like you're saying that another test has left a project around instead of doing an app.Stop(true, false)
or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not another test - this same test. It's running ddev config
in a loop in the same directory. It is doing app.Stop(true, false)
but that seems to be insufficient to remove the project from the project list file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If app.Stop(true,false) isn't removing the project from the project list file, it's an introduced bug. Every single test would fail if that were common. Please step through in debugger and figure out what's going on. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know - I'll introduce a new test that explicitly tests app.Stop(true,false)
and debug it. If such a test already existed I wouldn't have come up with this solution since it would have been clear what I found was a red herring.
I'll try to find time this week to do this.
cmd/ddev/cmd/config_test.go
Outdated
@@ -514,6 +514,8 @@ func TestConfigDatabaseVersion(t *testing.T) { | |||
// add a database into the config and nothing else | |||
for _, dbTypeVersion := range versionsToTest { | |||
_ = app.Stop(true, false) | |||
_, err := exec.RunHostCommand(DdevBin, "delete", "-Oy", t.Name()) | |||
assert.NoError(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been leaning more toward require() lately, which stops the test. Less time chasing irrelevant things. It's not necessary to accept this, I'm just commenting.
assert.NoError(err) | |
require.NoError(t, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth making a push to change all of them at some stage and get CI to catch that.
I'll change this if you want me to use the omitempty
discussed in all the other comments - but if you're happy leaving that as is then I'll leave this as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not important. You're just such an important contributor that I'm doing a bit of coaching. Nothing wrong with what's here.
pkg/globalconfig/global_config.go
Outdated
@@ -33,6 +32,8 @@ var ( | |||
DdevGlobalConfig GlobalConfig | |||
// DdevProjectList is the list of all existing DDEV projects | |||
DdevProjectList map[string]*ProjectInfo | |||
// Old versions of DDEV stored the project list in the global config. | |||
ddevLegacyGlobalConfig LegacyGlobalConfig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting technique, but couldn't we just allow the old ProjectList in current config, but not write it out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that's what the omitempty
does in #5926 (review)?
If so, I have no problem doing that. I just wasn't aware that was an option.
When I tried doing it without omitempty
it ended up with an empty map in the yaml file which is obviously misleading - for anyone who looks at that file it'd look like DDEV has incorrectly said there's no projects!
pkg/globalconfig/global_config.go
Outdated
// the new project_list.yaml | ||
// In a future release, remove this. | ||
type LegacyGlobalConfig struct { | ||
ProjectList map[string]*ProjectInfo `yaml:"project_info"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, tempted to allow the ProjectList in current global config, just not write it (just make sure it's empty and omitempty
I did manually test and look through, and it works perfectly as is. |
Let me know if you want the |
I would really appreciate it if you could experiment with that approach. I think it might be cleaner. |
Sweet, I'll try make some time this week to get that done. |
e34353a
to
9785509
Compare
Rebased, fixed merge conflicts. |
@stasadev or I will experiment with the omitempty if you don't have time for it and that's OK with you. Want to get this in this week. Thanks! |
Sweet, yes please do. |
9785509
to
46aafe9
Compare
@GuySartorelli, your idea was in the right direction, the project list was not updated, when it was needed. That's because we had I rewrote
Done, running tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed this again and manually tested, and it looks great to me! Thanks so much! 🙏🏼
The Issue
Users have been confused by having two copies of the project list, and haven't known which to update if they need to manually update the list.
How This PR Solves The Issue
project_list.yaml
file and theglobal_config.yaml
file contains a project list (i.e. if someone is upgrading from an older version of DDEV), copy the list intoproject_list.yaml
global_config.yaml
if there was any difference between the two listsProjectList
from theGlobalConfig
typeglobal_config.yaml
is written, the project list will be removed from that file.Note that I have done this in two commits to show why some of the changes are necessary. I'll add PR comments to help clarify this.
Manual Testing Instructions
project_list.yaml
if you have itddev list
- it should list the project(s) correctly, regardless of what state they were inproject_list.yaml
filelast_started_version
has changed, runddev start
in a project)global_config.yaml
no longer has a list pf projectsAutomated Testing Overview
No automated tests need to change - they were all already relying on
project_list.yaml
.Related Issue Link(s)
Release/Deployment Notes
This shouldn't affect anyone other than reducing confusion.