#1695: Clone settings to temporary directory, analyse, and then move#1878
#1695: Clone settings to temporary directory, analyse, and then move#1878areinicke wants to merge 19 commits into
Conversation
Coverage Report for CI Build 25993814010Coverage decreased (-0.2%) to 70.496%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions51 previously-covered lines in 5 files lost coverage.
Coverage Stats💛 - Coveralls |
…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>
|
Thanks for the review. I have integrated the changes. |
hohwille
left a comment
There was a problem hiding this comment.
@areinicke thanks for your PR. Great that you implemented a test for it and finally started improving GitContextMock what I suggested long time ago when GitContextMock was supposed to be implemented. 👍
This is not a very easy task. Seems in such cases it makes sense to start with a design session before doing the complete PR implementation in the future...
However, should still not be a big change since most of your PR is already perfectly correct.
|
|
||
| /** | ||
| * This method is invoked when a new porject is created. It analyzes the cloned repository to check if it is a valid IDEasy repository. The repository can 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 creatio fails and an error message is logged. |
There was a problem hiding this comment.
| * or a code repository (with a settings folder on the top level containing such a file). Otherwise, the project creatio fails and an error message is logged. | |
| * 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. |
| /** | ||
| * Moves files of a new projectfrom the temporary location to the final project location. | ||
| * @param oldPath - The path of the file or directory to be moved. | ||
| * @param newPath - The path of the destination. | ||
| */ | ||
| private void moveProject(Path oldPath, Path newPath) { | ||
| FileAccess fileAccess = this.context.getFileAccess(); | ||
| try { | ||
| fileAccess.copy(oldPath, newPath, FileCopyMode.COPY_TREE_OVERRIDE_FILES); | ||
| fileAccess.delete(oldPath); | ||
| } 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 am not getting why we move by creating a copy and then deleting the original source when we could use an atomic move operation. A clone code-repo can potentially be very big - this seems wasteful.
| * @param repositoryPath - The path of the repository to be checked. | ||
| */ | ||
| private boolean isCodeRepository(Path repositoryPath) { | ||
| return Files.exists(repositoryPath.resolve("settings").resolve(EnvironmentVariables.DEFAULT_PROPERTIES)) || Files.exists(repositoryPath.resolve("settings").resolve(EnvironmentVariables.LEGACY_PROPERTIES)); |
There was a problem hiding this comment.
Reuse and avoid redundancies:
| return Files.exists(repositoryPath.resolve("settings").resolve(EnvironmentVariables.DEFAULT_PROPERTIES)) || Files.exists(repositoryPath.resolve("settings").resolve(EnvironmentVariables.LEGACY_PROPERTIES)); | |
| return isSettingsRepository(repositoryPath.resolve(IdeContext.FOLDER_SETTINGS)); |
| // Check if instance of create commandlet. Only then will we analyze the project | ||
| if (this instanceof CreateCommandlet) { | ||
| analyzeProject(); | ||
| } |
There was a problem hiding this comment.
This design can be improved.
Option 1: you assume that you only need to analyse this when a project is initially created but not when updated.
Then simply override updateSettings() in CreateCommandlet and call analyseProject() after super.updateSettings(). Then only CreateCommandlet knows of analyseProject() (SoC).
Option 2: you need to analyse whenever you need to clone the settings.
Then this code is wrong since you skip this in case of UpdateCommandlet.
In such case you would need to do this after the cloning of the settings.
IMHO the option to ask for the settings in case of ide update is a kind of auto-repair feature of IDEasy.
Therefore ide update should behave also in the same way that it detects if we cloned an regular settings repo or a code repo with settings included.
But to get all this clean, we might need to revisit the design how create and update try to reuse the existing code/methods from AbstractUpdateCommandlet.
IMHO it would be better to do this on create:
- prepare a temp folder
- trigger the reusable functionality that clones the settings into a given project folder where here we provide the temporary folder. If it detected a code repo, it can move the settings to main workspace and proper folder name from the GitUrl and create the symlink to preserve a proper
settingsfolder in the projects root dir. - if that fails (neither regular settings repo nor code repo with settings found) we cleanup and abort.
- if that succeeded we can move the temp folder to the real project folder and continue with the regular logic where we re-initialize the IdeContext on the real project and do the full create/update process.
In case of ide update we can keep everything as is and reuse the same functionality described above in the 2nd point.
| } else if (isCodeRepository(settingsPath)) { | ||
| 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."); | ||
| // Move settings folder contents containing code into workspace/main/<project_name> | ||
| actualProjectPath = this.context.getIdeRoot().resolve(projectName).resolve("workspaces/main/").resolve(projectName); |
There was a problem hiding this comment.
The name should be derived from the git repo (see GitUrl)?
If I create ide create cool-project https://github.com/foo/awesome-project.git
I would expect $IDE_ROOT/cool-project/workspaces/main/awesome-project/settings and $IDE_ROOT/cool-project/settings being a symlink to it.
| // Delete empty settings folder in IDE_ROOT/<project_name> so we can create a symlink in the next step | ||
| fileAccess.delete(actualProjectPath.resolve(projectName).resolve("settings")); |
There was a problem hiding this comment.
move it, then you do not have to delete it.
| // Delete empty settings folder in IDE_ROOT/<project_name> so we can create a symlink in the next step | |
| fileAccess.delete(actualProjectPath.resolve(projectName).resolve("settings")); |
| // Final cleanup in temp location | ||
| fileAccess.delete(this.context.getIdeHome()); | ||
|
|
There was a problem hiding this comment.
IMHO again does not belong here due to SoC:
| // Final cleanup in temp location | |
| fileAccess.delete(this.context.getIdeHome()); |
| // Repository seems to be invalid. Clean up temporary location and return error | ||
| fileAccess.delete(this.context.getIdeHome()); | ||
| throw new CliException("This repository does not include an " + EnvironmentVariables.DEFAULT_PROPERTIES + " or " + EnvironmentVariables.LEGACY_PROPERTIES + " file at the top level or a settings folder with such a file. " | ||
| + "The repository does not seem to be a valid IDEasy repository. Please verify the repository and try again."); |
There was a problem hiding this comment.
dont delete the entire project.
You can either delete the settings here or IMHO even better would be to just return failure (e.g. false while otherwise true is returned for success) and let the calling logic handle the cleanup so in case of ide update we can just delete the settings and in case of ide create we can delete the temp project.
| } | ||
| String userPromt = "Repository URL [" + IdeContext.DEFAULT_SETTINGS_REPO_URL + "]:"; | ||
| String defaultUrl = IdeContext.DEFAULT_SETTINGS_REPO_URL; | ||
| LOG.info(MESSAGE_SETTINGS_REPO_URL, this.context.getSettingsPath()); |
There was a problem hiding this comment.
fine but you then also need to include an update to the documentation that otherwise gets inconsistent:
https://github.com/devonfw/IDEasy/blob/main/documentation/settings.adoc#code-repository
| protected String getStepMessage() { | ||
|
|
||
| return "Create (clone) " + (isCodeRepository() ? "code" : "settings") + " repository"; | ||
| return "Creating (Cloning) repository"; |
There was a problem hiding this comment.
For steps we always use infinitive (e.g. to add prefixes in log message like starting step ..., step ... failed, or even failed to ...)
| return "Creating (Cloning) repository"; | |
| return "Create (clone) repository"; |
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