Move .solution.json to hidden subdirectory#630
Move .solution.json to hidden subdirectory#630jdsutherland wants to merge 14 commits intoexercism:masterfrom
Conversation
|
It seems the AppVeyor error is the same as #557. Is the intended solution to rename .solution.json, (i.e., |
Yeah we're aiming for |
| { | ||
| desc: "It creates the .solution.json file.", | ||
| path: filepath.Join(cmdTest.TmpDir, "bogus-track", "bogus-exercise", ".solution.json"), | ||
| path: filepath.Join(cmdTest.TmpDir, "bogus-track", "bogus-exercise", "./.exercism/.solution.json"), |
There was a problem hiding this comment.
We can't hard-code slashes here, as on Windows the slashes go the other way.
filepath.Join(cmdTest.TmpDir, "bogus-track", "bogus-exercise", ".exercism", "solution.json")
To be clear, the path on *nix will be |
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| const hiddenSolutionDir = ".exercism" |
There was a problem hiding this comment.
This is not being used anywhere. Is it needed?
There was a problem hiding this comment.
Oops, not sure how that slipped through.
| const ignoreSubdir = ".exercism" | ||
| const solutionFilename = ".solution.json" | ||
|
|
||
| var solutionRelPath = filepath.Join(ignoreSubdir, solutionFilename) |
There was a problem hiding this comment.
Seems like this is only used within NewSolution does it need to be a package level variable?
There was a problem hiding this comment.
It gets used in 3 places (including workspace/workspace.go); it seems worth doing to encapsulate the path in one place. Is inlining const ignoreSubdir preferred?
There was a problem hiding this comment.
Im speaking about solutionRelPath. I did a quick search and didn’t see other references. If that variable is used three times and we don’t plan on changing it for testing should it be const?
There was a problem hiding this comment.
It can't be const: [gometalinter] const initializer filepath.Join(ignoreSubdir, solutionFilename) is not a constant (vet) [Error]
There was a problem hiding this comment.
Sorry I wasn’t clear, if we make solutionFilename a const with the new path we wouldn’t need the other variables as they are just being used to construct the final solution path
const SolutionFilename = filepath.Join(“.exercism”,”solution.json”)
I also think it should be exported to denote that it is actually shared or is expected to be.
There was a problem hiding this comment.
Gotcha. It appears that filepath.Join cannot be const: [gometalinter] const initializer filepath.Join(".exercism", "solution.json") is not a constant (vet) [Error]
There was a problem hiding this comment.
Well then it seems like the best advise I can give is to carry on as you were. Disregarding my coments on this file.
I didnt think about the the separator not being known until run time making it unfit to use as a const, my fault.
Thanks for vetting and for your time.
There was a problem hiding this comment.
No problem. I appreciate your feedback.
I was thinking we don't need the file to be hidden if it's in a hidden directory. So:
|
How strongly do we feel that the metadata directory needs to be hidden? |
Address review concerns: * fix path string OS compatibility * remove dead var
|
Something to think about. When we roll this change out it will break already downloaded exercises as the solution json file will be in a new path. Forcing them to redownload to fix. If that is the case then our code should detect if an existing solution file exists and move it for the user. Thoughts? |
|
How smooth will the transition be from |
Yes, I was hoping to get this sorted before the launch, but it's been an eventful few days and I didn't get to it. We need to handle detecting the old location and moving the file for the user if it is found. |
Adds compatibility for already downloaded exercises
eb77349 to
b16fe6e
Compare
I gave this a shot and think it's in a good state for review.
Do we want to remove |
3d1ce31 to
4dd5ab5
Compare
4dd5ab5 to
34c4ec6
Compare
|
@jdsutherland I’ve been a little caught up. Thanks for updating. I’ll take a look at your latest changes and get back to you by tomorrow. |
e54f2ad to
53ea687
Compare
golint: exported const SolutionFilename should have comment or be unexported [Warning]
53ea687 to
858abd2
Compare
Export the joined filepath of the solution metadata file rather than the individual paths
|
@jdsutherland I haven’t forgotten about this PR. Thanks for keeping it updated. I will tackle this PR with the team PR later today. |
nywilken
left a comment
There was a problem hiding this comment.
Do you mean remove all refs of visibility package? Isn't visibility.HideFile(path) needed to hide the directory in Windows?
That was the question I was asking and it seems like it may not be needed now that tests are passing on Windows. We need to thoroughly test with an actual build and file downloads.
nywilken
left a comment
There was a problem hiding this comment.
I had a chance to dive deeper into this review. I left a number of comments and called out a few extra eyes to help answer some open questions. This is just about ready IMO. @FabienTregan if you have a minute or two it would be great to get your eyes on the file visibility code that was removed, as it appears to be no longer needed.
| assert.Regexp(t, "No files found", err.Error()) | ||
| } | ||
|
|
||
| func TestSubmitExerciseWithLegacySolutionMetadataFileAndGetsMigrated(t *testing.T) { |
There was a problem hiding this comment.
What do you think about calling this method TestLegacySolutionMetadataMigration?
There was a problem hiding this comment.
TestLegacySolutionMetadataMigration is more readable. The only potential concern I can think of is that the test isn't just testing migration but that a submission is still successful given a legacy solution file - is it worth indicating that in the name?
|
|
||
| tmpDir, err := ioutil.TempDir("", "legacy-metadata-file") | ||
| assert.NoError(t, err) | ||
|
|
There was a problem hiding this comment.
These files are not being cleaned up after the tests. A deferred call os.RemoveAll should do the trick.
| Dir: tmpDir, | ||
| UserViperConfig: v, | ||
| } | ||
| expectedSolutionPathAfterMigration := filepath.Join(dir, workspace.SolutionMetadataFilepath()) |
There was a problem hiding this comment.
Does it make sense to check that this file doesn’t exist prior to executing submit?
@kytrinyx thoughts, Is it too much?
There was a problem hiding this comment.
Yeah, I think it would help clarify intent and ensure completeness.
|
|
||
| // Write stores solution metadata to a file. | ||
| func (s *Solution) Write(dir string) error { | ||
| func (s *Solution) Write(path string) error { |
There was a problem hiding this comment.
Sorry for the confusion this variable should remain as dir in a previous version you were generating the path, but still using dir to concat filenames.
I think what you have now is good. I just recommend that you keep dir for the exercise directory and use path for the SolutionMetadataPath
| return err | ||
| } | ||
| s.Dir = path | ||
| return err |
There was a problem hiding this comment.
return nil is a bit more clear that no error is being returned
| fmt.Fprintf(os.Stderr, "\nMigrated solution metadata to %s\n", solutionPath) | ||
| } else { | ||
| // TODO: decide how to handle case where both legacy and modern metadata files exist | ||
| fmt.Fprintf(os.Stderr, "\nAttempted to migrate solution metadata to %s but file already exists\n", solutionPath) |
There was a problem hiding this comment.
I think the new metadata takes precendence which leads into my next comment about workspace,checkSolutionFile. It currently checks for a legacy metdata first, but I think we should check for the new path first and only check for legacy if it doesn’t exist.
There was a problem hiding this comment.
Right. The most common path should be checked first.
| solutionPath := filepath.Join(path, SolutionMetadataFilepath()) | ||
|
|
||
| if _, err := os.Lstat(legacySolutionPath); err == nil { | ||
| return migrateLegacySolutionFile(legacySolutionPath, solutionPath) |
There was a problem hiding this comment.
* rename test * test artifact cleanup * during test, check expected migration path DNE before migration * when checking solution files, check for a modern solution before legacy
6ac00a2 to
aa41c17
Compare
aa41c17 to
5a641a5
Compare
If testing on Windows is successful, should the |
881a90b to
9a2c772
Compare
Yes, if we aren't referring to it, I think we should delete it. |
| } | ||
|
|
||
| // SolutionMetadataFilepath is the path of the solution metadata file relative to the workspace. | ||
| func SolutionMetadataFilepath() string { |
There was a problem hiding this comment.
I don't understand how this works. Each exercise has its own metadata file, so this is relative to a subdirectory within the workspace.
There was a problem hiding this comment.
I mistakenly used 'workspace' when I meant 'exercise' there.
|
I've started working on a change in #682 that I think would help clarify things here. With the
I think it would be worth making the change in multiple steps, for clarity.
Does that make sense? |
|
Yeah, that makes sense. I think what you've suggested will improve clarity. It seems that the terms 'exercise' and 'solution' are used interchangeably in certain places but are defined differently based on https://github.com/exercism/docs/blob/master/about/glossary.md. A few examples: Lines 21 to 23 in d48268d Lines 28 to 32 in d48268d If I understand correctly, the metadata is static - it's generated once when the user runs a download cmd and doesn't change when a solution gets submitted. If it doesn't change when a solution is submitted, calling it SolutionMetadata seems counterintuitive. When I first began making these changes (as someone unfamiliar with the codebase and terminology) I expected This confusion might stem from the meaning of 'solution'. Based on the glossary definition, I'd expect a 'solution' to be one or more iterations of an exercise - work done to solve an exercise. Currently, a Solution "has-a" Exercise. Solution appears to represent both the exercise itself (a Solution type is created when an exercise is downloaded for the first time) and an iteration of that exercise. So, the user downloads a 'solution', does work to solve it, and the result is also called a 'solution'. I find this confusing. Maybe I'm not thinking about this clearly. |
I think you're right that |
|
@jdsutherland How are you getting on? I think we decided to not rename the solution and exercise metadata until after this work is done. As far as I can tell, the next step would be to open a separate PR that ensures that we never hard-code |
Awesome, I'll review that now! |
Closes #607
Closes #557