Skip to content
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

#757: Settings in code repository #850

Merged
merged 36 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
84a29cf
did a little bit of everything, javadoc and tests missing
salimbouch Dec 5, 2024
f73b3fe
added documentation
salimbouch Dec 5, 2024
6923f71
tiny changes
salimbouch Dec 5, 2024
0e09cbe
Merge branch 'main' into settings-in-code-repo
salimbouch Dec 5, 2024
d4a5590
Update GitContextMock.java
salimbouch Dec 5, 2024
11f372d
Merge branch 'settings-in-code-repo' of https://github.com/salimbouch…
salimbouch Dec 5, 2024
5eb994f
Update GitContextMock.java
salimbouch Dec 5, 2024
cb46e0e
review changes
salimbouch Dec 9, 2024
0284234
review changes
salimbouch Dec 9, 2024
d8e4e88
moved save commit Id method to IdeContext
salimbouch Dec 9, 2024
d67de02
review changes
salimbouch Dec 11, 2024
094836e
removed unused import
salimbouch Dec 11, 2024
7523af6
fixed nls tests
salimbouch Dec 12, 2024
5b266fe
fixed tests
salimbouch Dec 12, 2024
5de9a0f
fixed nls test
salimbouch Dec 12, 2024
a7b5ec2
Merge branch 'main' into settings-in-code-repo
salimbouch Dec 12, 2024
cbf6cdc
fixed 826
salimbouch Dec 12, 2024
446475f
imports
salimbouch Dec 12, 2024
d112979
Update AbstractIdeContext.java
salimbouch Dec 12, 2024
ff61a2b
added check so that tests dont break
salimbouch Dec 12, 2024
845c9d9
Merge branch 'main' into settings-in-code-repo
salimbouch Dec 12, 2024
1334373
Merge branch 'main' into settings-in-code-repo
salimbouch Dec 13, 2024
44ead0c
reviewed changes
salimbouch Dec 15, 2024
532e318
Merge branch 'main' into settings-in-code-repo
hohwille Dec 16, 2024
7b55a39
review changes
salimbouch Dec 16, 2024
5324365
review changes
salimbouch Dec 16, 2024
e465bae
more elaboration on --code usage
salimbouch Dec 16, 2024
5ea6d95
Merge branch 'main' into settings-in-code-repo
hohwille Dec 20, 2024
a1a7238
Update GitContextImpl.java: constructive review
hohwille Dec 20, 2024
af9e828
Merge branch 'main' of https://github.com/devonfw/ideasy into setting…
salimbouch Jan 5, 2025
0ddbef6
adjusted changelog
salimbouch Jan 5, 2025
79bbd25
Merge branch 'settings-in-code-repo' of https://github.com/salimbouch…
salimbouch Jan 5, 2025
165174c
added javadoc
salimbouch Jan 7, 2025
80ba24e
Merge branch 'main' into settings-in-code-repo
hohwille Jan 10, 2025
37ac585
Merge branch 'main' into settings-in-code-repo
hohwille Jan 14, 2025
065f174
Merge branch 'main' into settings-in-code-repo
hohwille Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.commandlet;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
Expand Down Expand Up @@ -115,6 +116,13 @@ private void updateSettings() {
// here we do not use pullOrClone to prevent asking a pointless question for repository URL...
if (Files.isDirectory(settingsPath) && !this.context.getFileAccess().isEmptyDir(settingsPath)) {
step = this.context.newStep("Pull settings repository");
if (!Files.isDirectory(settingsPath.resolve(GitContext.GIT_FOLDER))) {
try {
settingsPath = settingsPath.toRealPath();
} catch (IOException e) {
throw new IllegalStateException("Could not resolve settings folder to real path.", e);
}
}
gitContext.pull(settingsPath);
hohwille marked this conversation as resolved.
Show resolved Hide resolved
} else {
step = this.context.newStep("Clone settings repository");
Expand All @@ -132,6 +140,7 @@ private void updateSettings() {
}
gitContext.pullOrClone(GitUrl.of(repository), settingsPath);
}
gitContext.saveCurrentCommitId(settingsPath);
step.success("Successfully updated settings repository.");
} finally {
if (step != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.devonfw.tools.ide.commandlet;

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

import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.git.GitUrl;
import com.devonfw.tools.ide.io.FileAccess;
import com.devonfw.tools.ide.property.FlagProperty;
import com.devonfw.tools.ide.property.StringProperty;
Expand All @@ -18,6 +20,12 @@ public class CreateCommandlet extends AbstractUpdateCommandlet {
/** {@link FlagProperty} for skipping the setup of git repositories */
public final FlagProperty skipRepositories;

/** {@link FlagProperty} for creating a project with settings inside a code repository */
public final FlagProperty codeRepositoryFlag;

/** {@link StringProperty} for the URL of the given code repository */
public final StringProperty codeRepository;

/**
* The constructor.
*
Expand All @@ -28,6 +36,8 @@ public CreateCommandlet(IdeContext context) {
super(context);
this.newProject = add(new StringProperty("", true, "project"));
this.skipRepositories = add(new FlagProperty("--skip-repositories"));
this.codeRepositoryFlag = add(new FlagProperty("--code"));
this.codeRepository = add(new StringProperty("", false, "codeRepository"));
hohwille marked this conversation as resolved.
Show resolved Hide resolved
add(this.settingsRepo);
}

Expand Down Expand Up @@ -58,13 +68,40 @@ public void run() {

initializeProject(newProjectPath);
this.context.setIdeHome(newProjectPath);
if (codeRepositoryFlag.isTrue()) {
String repoUrl = codeRepository.getValue();
if (repoUrl.isEmpty()) {
this.context.info("Please provide a repository URL after using --code");
} else {
initializeCodeRepository(repoUrl);
}
}
super.run();
hohwille marked this conversation as resolved.
Show resolved Hide resolved

if (this.skipRepositories.isTrue()) {
this.context.info("Skipping the cloning of project repositories as specified by the user.");
} else {
updateRepositories();
}
this.context.success("Successfully created new project '{}'.", newProjectName);

}

private void initializeCodeRepository(String repoUrl) {

// clone the given repository into IDE_HOME/workspaces/main
GitUrl gitUrl = GitUrl.of(repoUrl);
Path codeRepoPath = this.context.getWorkspacePath().resolve(gitUrl.getProjectName());
this.context.getGitContext().pullOrClone(gitUrl, codeRepoPath);
hohwille marked this conversation as resolved.
Show resolved Hide resolved

// check for settings folder and create symlink to IDE_HOME/settings
Path settingsFolder = codeRepoPath.resolve(IdeContext.FOLDER_SETTINGS);
if (Files.exists(settingsFolder)) {
this.context.getFileAccess().symlink(settingsFolder, this.context.getSettingsPath());
}

// create a file in IDE_HOME with the current local commit id
this.context.getGitContext().saveCurrentCommitId(codeRepoPath);
}

private void initializeProject(Path newInstancePath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ private ValidationResult applyAndRun(CliArguments arguments, Commandlet cmd) {
if (this.settingsPath != null) {
if (getGitContext().isRepositoryUpdateAvailable(this.settingsPath) ||
(getGitContext().fetchIfNeeded(this.settingsPath) && getGitContext().isRepositoryUpdateAvailable(this.settingsPath))) {
interaction("Updates are available for the settings repository. If you want to pull the latest changes, call ide update.");
interaction("Updates are available for the settings repository. If you want to apply the latest changes, call \"ide update\"");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ public interface IdeContext extends IdeStartContext {
/** Legacy folder name used as compatibility fallback if {@link #FOLDER_TEMPLATES} does not exist. */
String FOLDER_LEGACY_TEMPLATES = "devon";

/**
* file containing the current local commit hash of the settings repository. */
String SETTINGS_COMMIT_ID = ".commit.id";

/**
* @return {@code true} if {@link #isOfflineMode() offline mode} is active or we are NOT {@link #isOnline() online}, {@code false} otherwise.
*/
Expand Down
11 changes: 9 additions & 2 deletions cli/src/main/java/com/devonfw/tools/ide/git/GitContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,20 @@ public interface GitContext {
* Checks if there are updates available for the Git repository in the specified target folder by comparing the local commit hash with the remote commit
* hash.
*
* @param repository the {@link Path} to the target folder where the git repository is located. This should be the folder containing the ".git"
* subfolder.
* @param repository the {@link Path} to the target folder where the git repository is located.
hohwille marked this conversation as resolved.
Show resolved Hide resolved
* @return {@code true} if the remote repository contains commits that are not present in the local repository, indicating that updates are available.
* {@code false} if the local and remote repositories are in sync, or if there was an issue retrieving the commit hashes.
*/
boolean isRepositoryUpdateAvailable(Path repository);


/**
* Saves the current git commit ID of a repository to a .commit.id file in IDE_HOME.
*
* @param repository the path to the git repository
*/
void saveCurrentCommitId(Path repository);

/**
* Attempts a git pull and reset if required.
*
Expand Down
35 changes: 31 additions & 4 deletions cli/src/main/java/com/devonfw/tools/ide/git/GitContextImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.git;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
Expand Down Expand Up @@ -51,12 +52,38 @@ public boolean fetchIfNeeded(Path repository, String remote, String branch) {
public boolean isRepositoryUpdateAvailable(Path repository) {

verifyGitInstalled();
String localCommitId = runGitCommandAndGetSingleOutput("Failed to get the local commit id.", repository, "rev-parse", "HEAD");
String remoteCommitId = runGitCommandAndGetSingleOutput("Failed to get the remote commit id.", repository, "rev-parse", "@{u}");
if ((localCommitId == null) || (remoteCommitId == null)) {
Path commitIdFile = this.context.getIdeHome().resolve(IdeContext.SETTINGS_COMMIT_ID);
if (!Files.exists(commitIdFile)) {
saveCurrentCommitId(repository);
}
String trackedCommitId;
try {
trackedCommitId = Files.readString(commitIdFile);
} catch (IOException e) {
return false;
}
return !localCommitId.equals(remoteCommitId);

String remoteCommitId = runGitCommandAndGetSingleOutput("Failed to get the remote commit id.", repository, "rev-parse", "@{u}");
return !trackedCommitId.equals(remoteCommitId);
}

@Override
public void saveCurrentCommitId(Path repository) {

if (!Files.exists(repository.resolve(GitContext.GIT_FOLDER))) {
try {
repository = repository.toRealPath();
} catch (IOException e) {
throw new IllegalStateException("Couldn't resolve settings to real path.", e);
}
hohwille marked this conversation as resolved.
Show resolved Hide resolved
}
String currentCommitId = runGitCommandAndGetSingleOutput("Failed to get current commit id.", repository, "rev-parse", "HEAD");
Path commitIdFile = this.context.getIdeHome().resolve(IdeContext.SETTINGS_COMMIT_ID);
try {
Files.writeString(commitIdFile, currentCommitId);
} catch (IOException e) {
throw new IllegalStateException("Failed to save commit ID", e);
}
}

@Override
Expand Down
15 changes: 15 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/git/GitUrl.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ public String toString() {
return this.url + "#" + this.branch;
}

/**
* Extracts the project name from an git URL.
* For URLs like "https://github.com/devonfw/ide-urls.git" returns "ide-urls"
*
* @return the project name without ".git" extension
*/
public String getProjectName() {
String path = this.url.substring(this.url.indexOf("://") + 3);
if (path.endsWith(".git")) {
path = path.substring(0, path.length() - 4);
}
String[] parts = path.split("/");
return parts[parts.length - 1];
}

/**
* @param gitUrl the {@link #toString() string representation} of a {@link GitUrl}. May contain a branch name as {@code «url»#«branch»}.
* @return the parsed {@link GitUrl}.
Expand Down
2 changes: 2 additions & 0 deletions cli/src/main/resources/nls/Help.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cmd.complete.detail=Run 'ide complete <args>' to activate the non-interactive au
cmd.create=Create a new IDEasy project.
cmd.create.detail=Creates a new IDEasy project in the projects directory (IDE_ROOT) using your custom project settings. If you want to have the default settings, you need to provide '-' as <settings_repository>. If you omit it, you will get an interactive question for the git settings URL.
cmd.create.val.project=The name of the new project that will be created.
cmd.create.val.codeRepository=The code repository to be given after --code
cmd.docker=Tool commandlet for Docker.
cmd.docker.detail=Docker is an open platform for creating, managing, and running applications in lightweight, isolated containers. Detailed documentation of the Docker Command Line Interface can be found at https://docs.docker.com/engine/reference/commandline/cli/
cmd.dotnet=Tool commandlet for dotnet.
Expand Down Expand Up @@ -121,6 +122,7 @@ opt.--offline=enable offline mode (skip updates or git pull, fail downloads or g
opt.--quiet=disable info logging (only log success, warning or error).
opt.--skip-repositories=skip the setup of repositories.
opt.--skip-tools=skip the installation/update of tools.
opt.--code=use git repository as both code and settings repository
opt.--skip-updates=disables tool updates if the configured versions match the installed versions.
opt.--trace=enable trace logging.
opt.--version=Print the IDE version and exit.
Expand Down
2 changes: 2 additions & 0 deletions cli/src/main/resources/nls/Help_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cmd.complete.detail=Geben Sie 'ide complete <ausdruck>' in die Konsole ein um di
cmd.create=Erstellt ein neues IDEasy Projekt.
cmd.create.detail=Erstellt ein neues IDEasy-Projekt im Projektverzeichnis (IDE_ROOT) unter Verwendung Ihrer benutzerdefinierten Projekteinstellungen. Wenn Sie die Standardeinstellungen verwenden möchten, müssen Sie '-' als <settings_repository> angeben. Wenn Sie '-' weglassen, erhalten Sie eine interaktive Abfrage für die Git-Settings-URL.
cmd.create.val.project=Der Name des zu erstellenden Projekts.
cmd.create.val.codeRepository=Das nach --code anzugebende Code-Repository
cmd.docker=Werkzeug Kommando für Docker.
cmd.docker.detail=Docker ist eine Plattform zum Erstellen, Verwalten und Ausführen von Anwendungen in isolierten Containern. Detaillierte Dokumentation zum Docker Command-Line Interface ist zu finden unter https://docs.docker.com/engine/reference/commandline/cli/
cmd.dotnet=Werkzeug Kommando für dotnet Kommandoschnittstelle.
Expand Down Expand Up @@ -121,6 +122,7 @@ opt.--offline=Aktiviert den Offline-Modus (Überspringt Aktualisierungen oder gi
opt.--quiet=Deaktiviert Info Logging ( nur success, warning und error).
opt.--skip-repositories=Überspringt die Einrichtung der Repositories.
opt.--skip-tools=Überspringt die Installation/Aktualisierung der Tools.
opt.--code=Git-Repository sowohl als Code- als auch als Settings-Repository verwenden
opt.--skip-updates=Deaktiviert Aktualisierungen von Tools wenn die installierten Versionen mit den konfigurierten Versionen übereinstimmen.
opt.--trace=Aktiviert Trace-Ausgaben (detaillierte Fehleranalyse).
opt.--version=Zeigt die IDE Version an und beendet das Programm.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ public boolean isRepositoryUpdateAvailable(Path repository) {
return false;
}

@Override
public void saveCurrentCommitId(Path repository) {

return;
}

@Override
public String determineCurrentBranch(Path repository) {

Expand Down
11 changes: 10 additions & 1 deletion documentation/settings.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@ To get an initial set of these settings we provide the default https://github.co
These are also released so you can download the latest stable or any history version at http://search.maven.org/#search|ga|1|a%3A%22devonfw-ide-settings%22[maven central].

To test `IDEasy` or for very small projects you can also use these the latest default settings (just hit return when link:setup.adoc[setup] is asking for the `Settings URL`).
However, for collaborative projects we strongly encourage you to distribute and maintain the settings via a dedicated and project specific `git` repository.
However, for collaborative projects we provide two approaches to distribute and maintain the settings:

* Via a dedicated and project specific git repository (recommended approach).
This gives you the freedom to control and manage the tools with their versions and configurations during the project lifecycle.
Therefore simply follow the link:usage.adoc#admin[admin usage guide].
* Via your code repository by including the settings folder directly in your project.
This allows you to keep settings changes in sync with code changes and manage them in the same pull requests.
To use this approach:
** Create a settings folder in your repository root following the structure described below
** Use `ide create --code <code-repo-url>` to create your project.
IDEasy will clone your repository and create a symlink to the settings folder.
Changes to settings can then be committed alongside code changes.

== Structure

Expand Down
Loading