#1695: Clone settings to temporary directory, analyse, and then move (taken over) - #2249
Conversation
…n' of https://github.com/areinicke/IDEasy into feature/1695-clone-settings-to-temp-dir-for-verification
Co-authored-by: Robin Wenzel <robin@die-wenzels.de>
…n' of https://github.com/areinicke/IDEasy into feature/1695-clone-settings-to-temp-dir-for-verification
…verification # Conflicts: # cli/src/test/java/com/devonfw/tools/ide/git/GitContextMock.java
…andlet Signed-off-by: laim2003 <luk.faber@gmx.de>
Signed-off-by: laim2003 <luk.faber@gmx.de>
48ef6c7 to
43ac0f2
Compare
Signed-off-by: laim2003 <luk.faber@gmx.de>
Signed-off-by: laim2003 <luk.faber@gmx.de>
Coverage Report for CI Build 30815427594Coverage decreased (-0.09%) to 72.518%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions40 previously-covered lines in 4 files lost coverage.
Coverage Stats💛 - Coveralls |
hohwille
left a comment
There was a problem hiding this comment.
@laim2003 thank you very much for taking this PR over 👍
It seems to remain complicated.
I tried to make my PoV clear for how we should design this.
However, I also found a problem with that regarding a link that may potentially not be relative. IMHO we should discuss about this together. Maybe you first have a look and think it over. If you find a clean and easy solution we are fine - otherwise lets have a quick call.
| if (Files.exists(tempProjectPath)) { | ||
| throw new CliException( | ||
| String.format("Temporary project directory already exists in: %s. Please delete it and try again.", tempProjectPath)); | ||
| } else if (Files.exists(newProjectPath)) { | ||
| throw new CliException( | ||
| String.format("Project directory already exists in: %s. As the project already exists, try calling 'ide update'.", | ||
| newProjectPath)); | ||
| } |
There was a problem hiding this comment.
To fail from the start if the final location (newProjectPath) already exists, make somehow sense.
However, keep in mind that before this PR the user got a warning and was asked if he wants to continue.
IMHO this new approach is also valid and we can skip the old behaviour.
This also makes it easier since before that was easy possible since we were adding files to the final project directory and now we create a new project in tmp and finally move it - so if the final project director already exists moving would not work...
However, regarding the existence of tempProjectPath I would simply use FileAccess.backup to get rid of it without further notice. This is in a temp directory so we should not fail, lets just get rid of it and start from scratch or do you fear that the same user by accident is calling two ide create commands for the same project? That would IMHO be stupidity of the end-user...
| if (!this.context.getFileAccess().isEmptyDir(newProjectPath)) { | ||
| this.context.askToContinue("Directory {} already exists. Do you want to continue?", newProjectPath); |
There was a problem hiding this comment.
If you throw an exception for this above this does not make sense any more.
| initializeProject(tempProjectPath); | ||
| this.context.setIdeHome(tempProjectPath); | ||
| super.doRun(); | ||
| this.context.getFileAccess().writeFileContent(IdeVersion.getVersionString(), newProjectPath.resolve(IdeContext.FILE_SOFTWARE_VERSION)); |
There was a problem hiding this comment.
Here I would expect the move from tempProjectPath to newProjectPath.
| * either be a settings repository (with ide.properties or devon.properties on the top level) or a code repository (with a settings folder on the top level | ||
| * containing such a file). Otherwise, the project creation fails and an error message is logged. | ||
| */ | ||
| private void analyzeProject() { |
There was a problem hiding this comment.
This method name is odd: As described in JavaDoc and what can be seen in the implementation, it does not "analyse the project" but "analyse the settings and potentially restructure them".
I would not expect a method with this name to do changes as side effect.
I would suggest to rename it to analyzeAndRestructureSettings().
Also this method should be moved to AbstractUpdateCommandlet since also the ide update command can repair the project so if settings folder is missing, it will ask for settings URL and clone the settings again. In that case we should invoke the same logic.
This method should however only move and restructure the settings to follow SoC and NOT move the entire project what IMHO belongs to doRun method that already has the tempProjectPath and newProjectPath variables and the responsibility of handling the tmp creation (and therefore also the according cleanup).
| LOG.info(EnvironmentVariables.DEFAULT_PROPERTIES + " or " + EnvironmentVariables.LEGACY_PROPERTIES | ||
| + " found in settings subfolder. This indicates a code repository with a settings folder on the top level."); | ||
|
|
||
| String gitProjectName = GitUrl.of(this.settingsRepo.getValue(0)).getProjectName(); |
There was a problem hiding this comment.
Indexed access shall only be used for multi-valued properties.
| String gitProjectName = GitUrl.of(this.settingsRepo.getValue(0)).getProjectName(); | |
| String gitProjectName = GitUrl.of(this.settingsRepo.getValue()).getProjectName(); |
| moveProject(this.context.getIdeHome(), actualProjectPath); | ||
|
|
||
| // Move settings fodler containing code to $IDE_ROOT/<project_name>/workspaces/main/<git_project_name> | ||
| moveProject(actualProjectPath.resolve(IdeContext.FOLDER_SETTINGS), codeFolderPath); |
There was a problem hiding this comment.
The settings are not a project.
Hence the method moveProject is named wrong.
| private void moveProject(Path oldPath, Path newPath) { | ||
| FileAccess fileAccess = this.context.getFileAccess(); | ||
| try { | ||
| fileAccess.mkdirs(newPath); | ||
| fileAccess.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING); | ||
| } catch (Exception e) { | ||
| LOG.error("Failed to move project from {} to {}. Please move it manually.", oldPath, newPath, e); | ||
| } | ||
| } |
There was a problem hiding this comment.
I do not understand the purpose of this method at all.
- Why do we do an
mkdirsto the target directory if we replace it anyways? Is that because the parent directory may not exist? Shouldn't this be handled byFileAccess.movethen? - REPLACE_EXISTING sounds dangerous to me. Do we really want to delete a target project that may have been created in
projectsfolder with the same name concurrently? - The exception handling is crazy: If the move failed we log and error and continue reporting the project creation was successful what is a lie and the implicit
icdto navigate to the newly created project will then fail? This is nuts. - So if we do nothing else in the end then calling
FileAccess.movewhy do we need a private method for that?I would simply remove this method.
| this.context.setIdeHome(actualProjectPath); | ||
|
|
||
| // Link settings folder in IDE_HOME to settings folder in code repository | ||
| fileAccess.symlink(codeFolderPath.resolve(IdeContext.FOLDER_SETTINGS), actualProjectPath.resolve(IdeContext.FOLDER_SETTINGS)); |
There was a problem hiding this comment.
Just one thought that I may have missed and that @areinicke never told me.
Is it possible that he did the moving of the project itself before this on purpose?
Assuming we are on Windows and we do not have permissions for proper symlinks, we will fallback to a junction - then IMHO this cannot be created relative.
In that case, we would create an absolute link here and following my review comments about SoC the resulting project would be broken if we move from tmp to projects after creating this absolute link.
Is that thought correct? Do we have a clean solution for this?
We have to keep in mind that we also need to solve and reuse this for the ide update command.
In the original PR we wanted to keep things simpler and after the massive review ping pongs and reworks, we decided to implement this in another go.
However, at least the design should keep that in mind.
Maybe it will not work out, if we do not implement and test these 2 aspects together...
| * https://github.com/devonfw/IDEasy/issues/1788[#1788]: Add Commandlet to create links | ||
| * https://github.com/devonfw/IDEasy/issues/797[#797]: Use system unzip on macOS to preserve symlinks in ZIP extraction | ||
| * https://github.com/devonfw/IDEasy/issues/1723[#1723]: Add commandlet for GitHub Copilot CLI | ||
| * https://github.com/devonfw/IDEasy/issues/1695[#1695]: Clone settings to temporary directory, analyse, and then move |
In my opinion, it is better if I complete the other tasks that I have open for the next release first, as in the MacOS installer, the console, the snapshot fix for the GUI. I can see that there is still a lot of structural points open in this PR here 🙂, therefore I believe it makes sense that I take some more time to deeply analyze the issues that we have here, especially since I need to dive deeper into understanding the requirements for this PR. If I am able to work on this issue before the next release, I will try to do that and then let you know about a call if I need help! |
This PR fixes #1695
Implemented changes:
$IDE_ROOT/_ide/tmp/projects/<project name>) where it is analyzed for validity. We check whether an ide.properties file exist either at the top level or within a settings folder at the top level. Only if this passes, do we create a new project at$IDE_ROOT/<project name>and move the files from the temporary location to the final location. The temporary folder is fully deleted after this process.Checklist for this PR
Make sure everything is checked before merging this PR. For further info please also see
our DoD.
mvn clean testlocally all tests pass and build is successful#«issue-id»: «brief summary»(e.g.#921: fixed setup.bat). If no issue ID exists, title only.In Progressand assigned to you or there is no issue (might happen for very small PRs)with
internal#Note: In some places, I used
getFileAccess()to copy and subsequently delete files. I am aware that there is a move function. However, this was always failing for some reason