Skip to content

Commit

Permalink
#982: ability for user specific workspace configuration templates (#1010
Browse files Browse the repository at this point in the history
)

Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com>
  • Loading branch information
hohwille and jan-vcapgemini authored Feb 7, 2025
1 parent 9c40fee commit 9c2006b
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 201 deletions.
16 changes: 12 additions & 4 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

This file documents all notable changes to https://github.com/devonfw/IDEasy[IDEasy].

== 2025.02.001

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/982[#982]: Ability for user specific IDE configuration

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/21?closed=1[milestone 2025.02.001].

== 2025.01.003

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/993[#993] Creation of start scripts for IDEs
* https://github.com/devonfw/IDEasy/pull/1003[#1003] graalvm compatibility mode to make x86-64 releases work on arm-64
* https://github.com/devonfw/IDEasy/issues/954[#954] Improve repository support
* https://github.com/devonfw/IDEasy/issues/993[#993] Creation of start scripts for IDEs
* https://github.com/devonfw/IDEasy/issues/993[#993]: Creation of start scripts for IDEs
* https://github.com/devonfw/IDEasy/pull/1003[#1003]: graalvm compatibility mode to make x86-64 releases work on arm-64
* https://github.com/devonfw/IDEasy/issues/954[#954]: Improve repository support
* https://github.com/devonfw/IDEasy/issues/993[#993]: Creation of start scripts for IDEs

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/20?closed=1[milestone 2025.01.003].

Expand Down
47 changes: 31 additions & 16 deletions cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.devonfw.tools.ide.io;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.List;
Expand Down Expand Up @@ -315,7 +312,18 @@ default void makeExecutable(Path file) {
* @param content the {@link String} with the text to write to a file.
* @param file the {@link Path} to the file where to save.
*/
void writeFileContent(String content, Path file);
default void writeFileContent(String content, Path file) {

writeFileContent(content, file, false);
}

/**
* @param content the {@link String} with the text to write to a file.
* @param file the {@link Path} to the file where to save.
* @param createParentDir if {@code true}, the parent directory will created if it does not already exist, {@code false} otherwise (fail if parent does
* not exist).
*/
void writeFileContent(String content, Path file, boolean createParentDir);

/**
* @param path that is checked whether it is a junction or not.
Expand All @@ -328,27 +336,34 @@ default void makeExecutable(Path file) {
* @return the parsed {@link Properties}.
*/
default Properties readProperties(Path file) {

Properties properties = new Properties();
try (Reader reader = Files.newBufferedReader(file)) {
properties.load(reader);
return properties;
} catch (IOException e) {
throw new IllegalStateException("Failed to read properties file: " + file, e);
}
readProperties(file, properties);
return properties;
}

/**
* @param file the {@link Path} to the {@link Properties} file to read.
* @param properties the existing {@link Properties} to {@link Properties#load(Reader) load} into.
*/
void readProperties(Path file, Properties properties);

/**
* @param properties the {@link Properties} to save.
* @param file the {@link Path} to the file where to save the properties.
*/
default void writeProperties(Properties properties, Path file) {

try (Writer writer = Files.newBufferedWriter(file)) {
properties.store(writer, null);
} catch (IOException e) {
throw new IllegalStateException("Failed to save properties file during tests.", e);
}
writeProperties(properties, file, false);
}


/**
* @param properties the {@link Properties} to save.
* @param file the {@link Path} to the file where to save the properties.
* @param createParentDir if {@code true}, the parent directory will created if it does not already exist, {@code false} otherwise (fail if parent does
* not exist).
*/
void writeProperties(Properties properties, Path file, boolean createParentDir);


}
33 changes: 32 additions & 1 deletion cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
Expand All @@ -31,6 +33,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
Expand Down Expand Up @@ -1001,8 +1004,11 @@ public String readFileContent(Path file) {
}

@Override
public void writeFileContent(String content, Path file) {
public void writeFileContent(String content, Path file, boolean createParentDir) {

if (createParentDir) {
mkdirs(file.getParent());
}
if (content == null) {
content = "";
}
Expand All @@ -1017,4 +1023,29 @@ public void writeFileContent(String content, Path file) {
throw new RuntimeException("Failed to write file " + file, e);
}
}

@Override
public void readProperties(Path file, Properties properties) {

try (Reader reader = Files.newBufferedReader(file)) {
properties.load(reader);
this.context.debug("Successfully loaded {} properties from {}", properties.size(), file);
} catch (IOException e) {
throw new IllegalStateException("Failed to read properties file: " + file, e);
}
}

@Override
public void writeProperties(Properties properties, Path file, boolean createParentDir) {

if (createParentDir) {
mkdirs(file.getParent());
}
try (Writer writer = Files.newBufferedWriter(file)) {
properties.store(writer, null); // do not get confused - Java still writes a date/time header that cannot be omitted
this.context.debug("Successfully saved {} properties to {}", properties.size(), file);
} catch (IOException e) {
throw new IllegalStateException("Failed to save properties file during tests.", e);
}
}
}
77 changes: 10 additions & 67 deletions cli/src/main/java/com/devonfw/tools/ide/merge/PropertiesMerger.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.devonfw.tools.ide.merge;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
Expand All @@ -11,6 +8,7 @@
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.environment.EnvironmentVariables;
import com.devonfw.tools.ide.environment.SortedProperties;
import com.devonfw.tools.ide.io.FileAccess;

/**
* Implementation of {@link FileMerger} for {@link Properties} files.
Expand All @@ -30,6 +28,7 @@ public PropertiesMerger(IdeContext context) {
@Override
protected void doMerge(Path setup, Path update, EnvironmentVariables resolver, Path workspace) {

FileAccess fileAccess = this.context.getFileAccess();
SortedProperties properties = new SortedProperties();
boolean updateFileExists = Files.exists(update);
Path template = setup;
Expand All @@ -38,61 +37,19 @@ protected void doMerge(Path setup, Path update, EnvironmentVariables resolver, P
this.context.trace("Nothing to do as update file does not exist: {}", update);
return; // nothing to do ...
}
load(properties, workspace);
fileAccess.readProperties(workspace, properties);
} else if (Files.exists(setup)) {
load(properties, setup);
fileAccess.readProperties(setup, properties);
}
if (updateFileExists) {
load(properties, update);
fileAccess.readProperties(update, properties);
template = update;
}
resolve(properties, resolver, template.toString());
save(properties, workspace);
fileAccess.writeProperties(properties, workspace, true);
this.context.trace("Saved merged properties to: {}", workspace);
}

/**
* @param file the {@link Path} to load.
* @return the loaded {@link Properties}.
*/
public Properties load(Path file) {

Properties properties = new Properties();
load(properties, file);
return properties;
}

/**
* @param file the {@link Path} to load.
* @return the loaded {@link Properties}.
*/
public Properties loadIfExists(Path file) {

Properties properties = new Properties();
if (file != null) {
if (Files.exists(file)) {
load(properties, file);
} else {
this.context.trace("Properties file does not exist: {}", file);
}
}
return properties;
}

/**
* @param properties the existing {@link Properties} instance.
* @param file the properties {@link Path} to load.
*/
public void load(Properties properties, Path file) {

this.context.trace("Loading properties file: {}", file);
try (Reader reader = Files.newBufferedReader(file)) {
properties.load(reader);
} catch (IOException e) {
throw new IllegalStateException("Could not load properties from file: " + file, e);
}
}

private void resolve(Properties properties, EnvironmentVariables variables, Object src) {

Set<Object> keys = properties.keySet();
Expand All @@ -102,21 +59,6 @@ private void resolve(Properties properties, EnvironmentVariables variables, Obje
}
}

/**
* @param properties the {@link Properties} to save.
* @param file the {@link Path} to save to.
*/
public void save(Properties properties, Path file) {

this.context.trace("Saving properties file: {}", file);
ensureParentDirectoryExists(file);
try (Writer writer = Files.newBufferedWriter(file)) {
properties.store(writer, null);
} catch (IOException e) {
throw new IllegalStateException("Could not write properties to file: " + file, e);
}
}

@Override
public void inverseMerge(Path workspace, EnvironmentVariables variables, boolean addNewProperties, Path update) {

Expand All @@ -129,8 +71,9 @@ public void inverseMerge(Path workspace, EnvironmentVariables variables, boolean
return;
}
Object src = workspace.getFileName();
Properties updateProperties = load(update);
Properties workspaceProperties = load(workspace);
FileAccess fileAccess = this.context.getFileAccess();
Properties updateProperties = fileAccess.readProperties(update);
Properties workspaceProperties = fileAccess.readProperties(workspace);
SortedProperties mergedProperties = new SortedProperties();
mergedProperties.putAll(updateProperties);
boolean updated = false;
Expand All @@ -150,7 +93,7 @@ public void inverseMerge(Path workspace, EnvironmentVariables variables, boolean
}
}
if (updated) {
save(mergedProperties, update);
fileAccess.writeProperties(mergedProperties, update);
this.context.debug("Saved changes from: {} to: {}", workspace.getFileName(), update);
} else {
this.context.trace("No changes for: {}", update);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.tool.ide;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;

Expand Down Expand Up @@ -60,27 +61,17 @@ public void runTool(String... args) {
*/
protected void configureWorkspace() {

Path settingsWorkspaceFolder = this.context.getSettingsPath().resolve(this.tool)
.resolve(IdeContext.FOLDER_WORKSPACE);
Path genericWorkspaceFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_WORKSPACE);
Path workspaceUpdateFolder = genericWorkspaceFolder.resolve(IdeContext.FOLDER_UPDATE);
Path workspaceSetupFolder = genericWorkspaceFolder.resolve(IdeContext.FOLDER_SETUP);
FileAccess fileAccess = this.context.getFileAccess();
if (!fileAccess.isExpectedFolder(settingsWorkspaceFolder)) {
return;
}
Path setupFolder = settingsWorkspaceFolder.resolve(IdeContext.FOLDER_SETUP);
Path updateFolder = settingsWorkspaceFolder.resolve(IdeContext.FOLDER_UPDATE);
if (!fileAccess.isExpectedFolder(setupFolder) && !fileAccess.isExpectedFolder(updateFolder)) {
return;
}
Path ideWorkspacePath = this.context.getWorkspacePath();
if (!fileAccess.isExpectedFolder(ideWorkspacePath)) {
Path workspaceFolder = this.context.getWorkspacePath();
if (!fileAccess.isExpectedFolder(workspaceFolder)) {
this.context.warning("Current workspace does not exist: {}", workspaceFolder);
return; // should actually never happen...
}
try (Step step = this.context.newStep("Configuring workspace " + ideWorkspacePath.getFileName() + " for IDE " + this.tool)) {
int errors = this.context.getWorkspaceMerger().merge(workspaceSetupFolder, workspaceUpdateFolder, this.context.getVariables(), ideWorkspacePath);
errors += this.context.getWorkspaceMerger().merge(setupFolder, updateFolder, this.context.getVariables(), ideWorkspacePath);
try (Step step = this.context.newStep("Configuring workspace " + workspaceFolder.getFileName() + " for IDE " + this.tool)) {
int errors = 0;
errors = mergeWorkspace(this.context.getUserHomeIde(), workspaceFolder, errors);
errors = mergeWorkspace(this.context.getSettingsPath(), workspaceFolder, errors);
errors = mergeWorkspace(this.context.getConfPath(), workspaceFolder, errors);
if (errors == 0) {
step.success();
} else {
Expand All @@ -93,6 +84,26 @@ protected void configureWorkspace() {
}
}

private int mergeWorkspace(Path configFolder, Path workspaceFolder, int errors) {

int result = errors;
result = mergeWorkspaceSingle(configFolder.resolve(IdeContext.FOLDER_WORKSPACE), workspaceFolder, result);
result = mergeWorkspaceSingle(configFolder.resolve(this.tool).resolve(IdeContext.FOLDER_WORKSPACE), workspaceFolder, result);
return result;
}

private int mergeWorkspaceSingle(Path templatesFolder, Path workspaceFolder, int errors) {

Path setupFolder = templatesFolder.resolve(IdeContext.FOLDER_SETUP);
Path updateFolder = templatesFolder.resolve(IdeContext.FOLDER_UPDATE);
if (!Files.isDirectory(setupFolder) && !Files.isDirectory(updateFolder)) {
this.context.trace("Skipping empty or non-existing workspace template folder {}.", templatesFolder);
return errors;
}
this.context.debug("Merging workspace templates from {}...", templatesFolder);
return errors + this.context.getWorkspaceMerger().merge(setupFolder, updateFolder, this.context.getVariables(), workspaceFolder);
}

/**
* Imports the repository specified by the given {@link Path} into the IDE managed by this {@link IdeToolCommandlet}.
*
Expand Down
Loading

0 comments on commit 9c2006b

Please sign in to comment.