This is an automated email from the ASF dual-hosted git repository.
mattcasters pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new d3287536e9 Issue #9089 : Add local run configurations to new Hop
setups (#9090)
d3287536e9 is described below
commit d3287536e91b4c032e231c26017cf6511f1b6373
Author: Matt Casters <[email protected]>
AuthorDate: Fri Oct 23 11:12:14 2026 +0200
Issue #9089 : Add local run configurations to new Hop setups (#9090)
* Issue #9089 : Add local run configurations to new Hop setups
Setup copied only the top level of the default project template, so a new
configuration was created without the local pipeline and workflow run
configurations. Copy the full template and add those configurations when
they are missing.
* Issue #9089 : Copy nested folders only for the default project template
---
.../modules/ROOT/pages/hop-tools/hop-setup.adoc | 2 +-
.../hop/setup/persist/ConfigFolderSeeder.java | 3 +-
.../org/apache/hop/setup/persist/HopVfsFiles.java | 18 +-
.../setup/persist/LocalRunConfigurationSeeder.java | 230 +++++++++++++++++++++
.../setup/persist/ProjectRegistrationWriter.java | 18 +-
.../setup/gui/messages/messages_en_US.properties | 2 +-
.../hop/setup/persist/ConfigFolderSeederTest.java | 14 ++
.../persist/LocalRunConfigurationSeederTest.java | 107 ++++++++++
.../persist/ProjectRegistrationWriterTest.java | 84 +++++++-
9 files changed, 471 insertions(+), 7 deletions(-)
diff --git a/docs/hop-user-manual/modules/ROOT/pages/hop-tools/hop-setup.adoc
b/docs/hop-user-manual/modules/ROOT/pages/hop-tools/hop-setup.adoc
index d8a9ebb7b9..85fbf9b394 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/hop-tools/hop-setup.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/hop-tools/hop-setup.adoc
@@ -86,7 +86,7 @@
image::hop-tools/setup/06-configure-hop-environment-dialog-projects-tab.png[Conf
** Windows: `%USERPROFILE%\Documents\Hop\default`
* Register the *samples* project from this Hop installation
(`<install>/config/projects/samples`) so the examples stay available after the
config folder moves.
-Both options are selected by default. Apply writes them into the new
`hop-config.json` (and creates `project-config.json` plus the install
default-project template when the default home is empty).
+Both options are selected by default. Apply writes them into the new
`hop-config.json`. An empty default project home is created from the install
default-project template. That project includes a `local` pipeline run
configuration and a `local` workflow run configuration. If the template does
not contain them, setup adds them, together with the local execution info
location and data profile they use.
=== Preview and apply
diff --git
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ConfigFolderSeeder.java
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ConfigFolderSeeder.java
index 15124e9e4c..fde50e0266 100644
---
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ConfigFolderSeeder.java
+++
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ConfigFolderSeeder.java
@@ -67,7 +67,8 @@ public class ConfigFolderSeeder {
result.addMessage("Would copy " + source + " to " +
spec.getConfigFolder());
return;
}
- HopVfsFiles.copyTree(source, spec.getConfigFolder());
+ // One level only: nested projects/default and projects/samples stay in
the install tree.
+ HopVfsFiles.copyChildren(source, spec.getConfigFolder());
result.addMessage("Copied install config from " + source + " to " +
spec.getConfigFolder());
}
diff --git
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/HopVfsFiles.java
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/HopVfsFiles.java
index 1eb951dcdc..123a0dfb44 100644
---
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/HopVfsFiles.java
+++
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/HopVfsFiles.java
@@ -21,6 +21,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSelector;
import org.apache.commons.vfs2.Selectors;
import org.apache.hop.core.vfs.HopVfs;
import org.apache.hop.setup.HopSetupException;
@@ -72,14 +73,29 @@ public final class HopVfsFiles {
}
}
+ /**
+ * Copies the direct children of {@code sourcePath} into {@code
destinationPath}. Child folders
+ * are created without their contents.
+ */
+ public static void copyChildren(String sourcePath, String destinationPath)
+ throws HopSetupException {
+ copySelected(sourcePath, destinationPath, Selectors.SELECT_CHILDREN);
+ }
+
+ /** Copies every file and subfolder under {@code sourcePath} into {@code
destinationPath}. */
public static void copyTree(String sourcePath, String destinationPath)
throws HopSetupException {
+ copySelected(sourcePath, destinationPath, Selectors.EXCLUDE_SELF);
+ }
+
+ private static void copySelected(String sourcePath, String destinationPath,
FileSelector selector)
+ throws HopSetupException {
try {
FileObject source = HopVfs.getFileObject(sourcePath);
FileObject destination = HopVfs.getFileObject(destinationPath);
if (!destination.exists()) {
destination.createFolder();
}
- destination.copyFrom(source, Selectors.SELECT_CHILDREN);
+ destination.copyFrom(source, selector);
} catch (Exception e) {
throw new HopSetupException(
"Unable to copy '" + sourcePath + "' to '" + destinationPath + "'",
e);
diff --git
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/LocalRunConfigurationSeeder.java
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/LocalRunConfigurationSeeder.java
new file mode 100644
index 0000000000..e8c0ec1d76
--- /dev/null
+++
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/LocalRunConfigurationSeeder.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.setup.persist;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hop.setup.HopEnvironmentApplyResult;
+import org.apache.hop.setup.HopSetupException;
+
+/**
+ * Adds the standard {@code local} pipeline and workflow run configurations to
a new project.
+ *
+ * <p>Files already present are left unchanged. A file that exists in the
install default-project
+ * template is copied from there. Otherwise the stock default-project content
is written, including
+ * the local execution info location and data profile those run configurations
use.
+ */
+public class LocalRunConfigurationSeeder {
+
+ static final String PIPELINE_RELATIVE =
"metadata/pipeline-run-configuration/local.json";
+ static final String WORKFLOW_RELATIVE =
"metadata/workflow-run-configuration/local.json";
+ static final String EXECUTION_INFO_RELATIVE =
"metadata/execution-info-location/local-audit.json";
+ static final String DATA_PROFILE_RELATIVE =
"metadata/execution-data-profile/first-last.json";
+
+ static final String PIPELINE_JSON =
+ """
+ {
+ "engineRunConfiguration": {
+ "Local": {
+ "feedback_size": "50000",
+ "sample_size": "100",
+ "sample_type_in_gui": "Last",
+ "rowset_size": "10000",
+ "safe_mode": false,
+ "show_feedback": false,
+ "topo_sort": false,
+ "gather_metrics": false
+ }
+ },
+ "name": "local",
+ "configurationVariables": [],
+ "description": "",
+ "executionInfoLocationName": "local-audit",
+ "dataProfile": "first-last",
+ "defaultSelection": true
+ }
+ """;
+
+ static final String WORKFLOW_JSON =
+ """
+ {
+ "engineRunConfiguration": {
+ "Local": {
+ "safe_mode": false
+ }
+ },
+ "name": "local",
+ "description": "",
+ "executionInfoLocationName": "local-audit",
+ "defaultSelection": true
+ }
+ """;
+
+ static final String EXECUTION_INFO_JSON =
+ """
+ {
+ "executionInfoLocation": {
+ "local-folder": {
+ "pluginName": "File location",
+ "pluginId": "local-folder",
+ "rootFolder": "${HOP_AUDIT_FOLDER}/executions/"
+ }
+ },
+ "dataLoggingDelay": "2000",
+ "name": "local-audit",
+ "description": "",
+ "dataLoggingInterval": "5000"
+ }
+ """;
+
+ static final String DATA_PROFILE_JSON =
+ """
+ {
+ "name": "first-last",
+ "description": "capture the first and last 100 rows of every Hop
transform",
+ "sampler": [
+ {
+ "FirstRowsExecutionDataSampler": {
+ "sampleSize": "100"
+ }
+ },
+ {
+ "LastRowsExecutionDataSampler": {
+ "sampleSize": "100"
+ }
+ }
+ ]
+ }
+ """;
+
+ public boolean standardRunConfigurationsMissing(String templateHome) throws
HopSetupException {
+ return !templateContains(templateHome, PIPELINE_RELATIVE)
+ || !templateContains(templateHome, WORKFLOW_RELATIVE);
+ }
+
+ public void ensure(String projectHome, String templateHome,
HopEnvironmentApplyResult result)
+ throws HopSetupException {
+ boolean synthesized =
+ ensureRunConfiguration(
+ projectHome,
+ templateHome,
+ PIPELINE_RELATIVE,
+ PIPELINE_JSON,
+ "local pipeline run configuration",
+ result);
+ synthesized =
+ ensureRunConfiguration(
+ projectHome,
+ templateHome,
+ WORKFLOW_RELATIVE,
+ WORKFLOW_JSON,
+ "local workflow run configuration",
+ result)
+ || synthesized;
+ if (synthesized) {
+ ensureFile(
+ projectHome,
+ templateHome,
+ EXECUTION_INFO_RELATIVE,
+ EXECUTION_INFO_JSON,
+ "local execution info location",
+ result);
+ ensureFile(
+ projectHome,
+ templateHome,
+ DATA_PROFILE_RELATIVE,
+ DATA_PROFILE_JSON,
+ "execution data profile",
+ result);
+ } else {
+ copyFromTemplateIfMissing(
+ projectHome,
+ templateHome,
+ EXECUTION_INFO_RELATIVE,
+ "local execution info location",
+ result);
+ copyFromTemplateIfMissing(
+ projectHome, templateHome, DATA_PROFILE_RELATIVE, "execution data
profile", result);
+ }
+ }
+
+ private boolean ensureRunConfiguration(
+ String projectHome,
+ String templateHome,
+ String relative,
+ String fallback,
+ String label,
+ HopEnvironmentApplyResult result)
+ throws HopSetupException {
+ String destination = projectHome + "/" + relative;
+ if (HopVfsFiles.exists(destination)) {
+ return false;
+ }
+ if (templateContains(templateHome, relative)) {
+ write(destination, HopVfsFiles.readUtf8(templateHome + "/" + relative),
label, result);
+ return false;
+ }
+ write(destination, fallback, label, result);
+ return true;
+ }
+
+ private void ensureFile(
+ String projectHome,
+ String templateHome,
+ String relative,
+ String fallback,
+ String label,
+ HopEnvironmentApplyResult result)
+ throws HopSetupException {
+ String destination = projectHome + "/" + relative;
+ if (HopVfsFiles.exists(destination)) {
+ return;
+ }
+ String content =
+ templateContains(templateHome, relative)
+ ? HopVfsFiles.readUtf8(templateHome + "/" + relative)
+ : fallback;
+ write(destination, content, label, result);
+ }
+
+ private void copyFromTemplateIfMissing(
+ String projectHome,
+ String templateHome,
+ String relative,
+ String label,
+ HopEnvironmentApplyResult result)
+ throws HopSetupException {
+ String destination = projectHome + "/" + relative;
+ if (HopVfsFiles.exists(destination) || !templateContains(templateHome,
relative)) {
+ return;
+ }
+ write(destination, HopVfsFiles.readUtf8(templateHome + "/" + relative),
label, result);
+ }
+
+ private static boolean templateContains(String templateHome, String relative)
+ throws HopSetupException {
+ return StringUtils.isNotBlank(templateHome)
+ && HopVfsFiles.exists(templateHome + "/" + relative);
+ }
+
+ private static void write(
+ String destination, String content, String label,
HopEnvironmentApplyResult result)
+ throws HopSetupException {
+ HopVfsFiles.writeUtf8(destination, content);
+ result.addMessage("Created " + label + " at " + destination);
+ }
+}
diff --git
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ProjectRegistrationWriter.java
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ProjectRegistrationWriter.java
index edb7d6bf6b..bfedb21a38 100644
---
a/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ProjectRegistrationWriter.java
+++
b/plugins/misc/setup/src/main/java/org/apache/hop/setup/persist/ProjectRegistrationWriter.java
@@ -35,8 +35,10 @@ import org.apache.hop.setup.UserPaths;
/**
* Creates a user-owned default project and registers it (plus the install
samples project) in the
- * target {@code hop-config.json}. Does not use the live {@code HopConfig}
singleton, which still
- * points at the current process config folder.
+ * target {@code hop-config.json}. A new default project gets a {@code local}
pipeline run
+ * configuration and a {@code local} workflow run configuration when it does
not already have them.
+ * Does not use the live {@code HopConfig} singleton, which still points at
the current process
+ * config folder.
*/
public class ProjectRegistrationWriter {
@@ -55,10 +57,17 @@ public class ProjectRegistrationWriter {
""";
private final ObjectMapper mapper;
+ private final LocalRunConfigurationSeeder runConfigurations;
public ProjectRegistrationWriter() {
+ this(new LocalRunConfigurationSeeder());
+ }
+
+ ProjectRegistrationWriter(LocalRunConfigurationSeeder runConfigurations) {
mapper = HopJson.newMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ this.runConfigurations =
+ runConfigurations == null ? new LocalRunConfigurationSeeder() :
runConfigurations;
}
public void apply(
@@ -118,6 +127,10 @@ public class ProjectRegistrationWriter {
hasTemplate
? "Would copy default project template to " + defaultHome
: "Would create default project at " + defaultHome);
+ if (runConfigurations.standardRunConfigurationsMissing(hasTemplate ?
template : null)) {
+ result.addMessage(
+ "Would add local pipeline and workflow run configurations to " +
defaultHome);
+ }
return;
}
HopVfsFiles.createFolder(defaultHome);
@@ -128,6 +141,7 @@ public class ProjectRegistrationWriter {
HopVfsFiles.writeUtf8(projectConfig, MINIMAL_PROJECT_CONFIG);
result.addMessage("Created default project at " + defaultHome);
}
+ runConfigurations.ensure(defaultHome, hasTemplate ? template : null,
result);
}
private String resolveSamplesHome(Path hopHome, HopEnvironmentApplyResult
result)
diff --git
a/plugins/misc/setup/src/main/resources/org/apache/hop/setup/gui/messages/messages_en_US.properties
b/plugins/misc/setup/src/main/resources/org/apache/hop/setup/gui/messages/messages_en_US.properties
index 1d5bda47f2..cd80d9ce51 100644
---
a/plugins/misc/setup/src/main/resources/org/apache/hop/setup/gui/messages/messages_en_US.properties
+++
b/plugins/misc/setup/src/main/resources/org/apache/hop/setup/gui/messages/messages_en_US.properties
@@ -26,7 +26,7 @@ SetupDialog.Web.ReadOnly=Hop Web cannot write the server
operating system enviro
SetupDialog.Tab.Variables=Variables
SetupDialog.Tab.Location=Location
SetupDialog.Tab.Projects=Projects
-SetupDialog.Projects.Intro=After you point Hop at a new config folder it
starts without projects. Create a default project in your documents folder and
register the samples project from this Hop installation.
+SetupDialog.Projects.Intro=After you point Hop at a new config folder it
starts without projects. Create a default project in your documents folder,
including a local pipeline and workflow run configuration, and register the
samples project from this Hop installation.
SetupDialog.Projects.CreateDefault=Create a default project
SetupDialog.Projects.DefaultHome.Label=Default project home
SetupDialog.Projects.Recommended=Use recommended project folder
diff --git
a/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ConfigFolderSeederTest.java
b/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ConfigFolderSeederTest.java
index 53a92bf374..496cb2b147 100644
---
a/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ConfigFolderSeederTest.java
+++
b/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ConfigFolderSeederTest.java
@@ -52,6 +52,17 @@ class ConfigFolderSeederTest {
Files.createDirectories(install.resolve("config"));
Files.writeString(install.resolve("config").resolve(HopSetupVariables.HOP_CONFIG_JSON),
"{}");
Files.writeString(install.resolve("config").resolve("extra.txt"), "keep");
+ Path defaultProject =
+ install
+
.resolve("config/projects/default/metadata/pipeline-run-configuration")
+ .resolve("local.json");
+ Files.createDirectories(defaultProject.getParent());
+ Files.writeString(defaultProject, "{ \"name\": \"local\" }");
+ Path samplesProject = install.resolve("config/projects/samples");
+ Files.createDirectories(samplesProject);
+ Files.writeString(
+ samplesProject.resolve(HopSetupVariables.PROJECT_CONFIG_FILENAME),
+ "{ \"description\": \"samples\" }");
Path target = temp.resolve("user-config");
HopEnvironmentSpec spec = new HopEnvironmentSpec();
spec.setConfigFolder(target.toString());
@@ -60,6 +71,9 @@ class ConfigFolderSeederTest {
new ConfigFolderSeeder().seed(spec, new HopEnvironmentApplyResult(),
install);
assertTrue(Files.exists(target.resolve(HopSetupVariables.HOP_CONFIG_JSON)));
assertTrue(Files.exists(target.resolve("extra.txt")));
+ assertTrue(Files.isDirectory(target.resolve("projects")));
+ assertFalse(Files.exists(target.resolve("projects/default")));
+ assertFalse(Files.exists(target.resolve("projects/samples")));
}
@Test
diff --git
a/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/LocalRunConfigurationSeederTest.java
b/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/LocalRunConfigurationSeederTest.java
new file mode 100644
index 0000000000..a6e731dece
--- /dev/null
+++
b/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/LocalRunConfigurationSeederTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.setup.persist;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.apache.hop.setup.HopEnvironmentApplyResult;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class LocalRunConfigurationSeederTest {
+
+ @TempDir Path temp;
+
+ @Test
+ void writesStandardLocalRunConfigurationsWhenTemplateHasNone() throws
Exception {
+ Path project = temp.resolve("default");
+ Files.createDirectories(project);
+ HopEnvironmentApplyResult result = new HopEnvironmentApplyResult();
+
+ new LocalRunConfigurationSeeder().ensure(project.toString(), null, result);
+
+ String pipeline =
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE));
+ String workflow =
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.WORKFLOW_RELATIVE));
+ assertTrue(pipeline.contains("\"name\": \"local\""));
+ assertTrue(pipeline.contains("\"defaultSelection\": true"));
+ assertTrue(pipeline.contains("\"executionInfoLocationName\":
\"local-audit\""));
+ assertTrue(pipeline.contains("\"dataProfile\": \"first-last\""));
+ assertTrue(workflow.contains("\"name\": \"local\""));
+ assertTrue(workflow.contains("\"defaultSelection\": true"));
+ assertTrue(
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.EXECUTION_INFO_RELATIVE))
+ .contains("${HOP_AUDIT_FOLDER}/executions/"));
+ assertTrue(
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.DATA_PROFILE_RELATIVE))
+ .contains("first-last"));
+ assertTrue(result.describe().contains("local pipeline run configuration"));
+ assertTrue(result.describe().contains("local workflow run configuration"));
+ }
+
+ @Test
+ void keepsTemplateRunConfigurationsAndDoesNotInventCompanions() throws
Exception {
+ Path template = temp.resolve("template");
+ write(
+ template, LocalRunConfigurationSeeder.PIPELINE_RELATIVE, "{ \"name\":
\"kept-pipeline\" }");
+ write(
+ template, LocalRunConfigurationSeeder.WORKFLOW_RELATIVE, "{ \"name\":
\"kept-workflow\" }");
+ Path project = temp.resolve("default");
+ Files.createDirectories(project);
+
+ new LocalRunConfigurationSeeder()
+ .ensure(project.toString(), template.toString(), new
HopEnvironmentApplyResult());
+
+ assertTrue(
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE))
+ .contains("kept-pipeline"));
+ assertTrue(
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.WORKFLOW_RELATIVE))
+ .contains("kept-workflow"));
+
assertFalse(Files.exists(project.resolve(LocalRunConfigurationSeeder.EXECUTION_INFO_RELATIVE)));
+
assertFalse(Files.exists(project.resolve(LocalRunConfigurationSeeder.DATA_PROFILE_RELATIVE)));
+ }
+
+ @Test
+ void doesNotReplaceExistingRunConfigurations() throws Exception {
+ Path project = temp.resolve("default");
+ write(project, LocalRunConfigurationSeeder.PIPELINE_RELATIVE, "{ \"name\":
\"mine\" }");
+ write(project, LocalRunConfigurationSeeder.WORKFLOW_RELATIVE, "{ \"name\":
\"mine-wf\" }");
+
+ new LocalRunConfigurationSeeder()
+ .ensure(project.toString(), null, new HopEnvironmentApplyResult());
+
+ assertTrue(
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE))
+ .contains("mine"));
+ assertTrue(
+
Files.readString(project.resolve(LocalRunConfigurationSeeder.WORKFLOW_RELATIVE))
+ .contains("mine-wf"));
+
assertFalse(Files.exists(project.resolve(LocalRunConfigurationSeeder.EXECUTION_INFO_RELATIVE)));
+ }
+
+ private static void write(Path root, String relative, String content) throws
Exception {
+ Path file = root.resolve(relative);
+ Files.createDirectories(file.getParent());
+ Files.writeString(file, content);
+ }
+}
diff --git
a/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ProjectRegistrationWriterTest.java
b/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ProjectRegistrationWriterTest.java
index d80345b54b..b440502945 100644
---
a/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ProjectRegistrationWriterTest.java
+++
b/plugins/misc/setup/src/test/java/org/apache/hop/setup/persist/ProjectRegistrationWriterTest.java
@@ -79,6 +79,12 @@ class ProjectRegistrationWriterTest {
hopHome);
assertTrue(Files.exists(defaultHome.resolve(HopSetupVariables.PROJECT_CONFIG_FILENAME)));
+ assertTrue(
+
Files.readString(defaultHome.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE))
+ .contains("\"name\": \"local\""));
+ assertTrue(
+
Files.readString(defaultHome.resolve(LocalRunConfigurationSeeder.WORKFLOW_RELATIVE))
+ .contains("\"name\": \"local\""));
String hopConfig =
Files.readString(configFolder.resolve(HopSetupVariables.HOP_CONFIG_JSON));
assertTrue(hopConfig.contains("\"projectName\" : \"default\""));
assertTrue(hopConfig.contains(defaultHome.toString()));
@@ -87,6 +93,78 @@ class ProjectRegistrationWriterTest {
assertTrue(hopConfig.contains("\"defaultProject\" : \"default\""));
}
+ @Test
+ void copiesNestedTemplateRunConfigurations() throws Exception {
+ Path hopHome = temp.resolve("hop");
+ Path template = hopHome.resolve("config/projects/default");
+
Files.createDirectories(template.resolve("metadata/pipeline-run-configuration"));
+
Files.createDirectories(template.resolve("metadata/workflow-run-configuration"));
+ Files.writeString(
+ template.resolve(HopSetupVariables.PROJECT_CONFIG_FILENAME),
+ "{ \"description\": \"template\" }");
+ Files.writeString(
+ template.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE),
+ "{ \"name\": \"from-template\" }");
+ Files.writeString(
+ template.resolve(LocalRunConfigurationSeeder.WORKFLOW_RELATIVE),
+ "{ \"name\": \"from-template-wf\" }");
+
+ Path defaultHome = temp.resolve("Documents/hop/default");
+ HopEnvironmentSpec spec = new HopEnvironmentSpec();
+ spec.setConfigFolder(temp.resolve("user-config").toString());
+ spec.setCreateDefaultProject(true);
+ spec.setDefaultProjectHome(defaultHome.toString());
+
+ new ProjectRegistrationWriter()
+ .apply(
+ spec,
+ OsFamily.UNIX,
+ unixPaths(temp.resolve("home")),
+ new HopEnvironmentApplyResult(),
+ hopHome);
+
+ assertTrue(
+
Files.readString(defaultHome.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE))
+ .contains("from-template"));
+ assertTrue(
+
Files.readString(defaultHome.resolve(LocalRunConfigurationSeeder.WORKFLOW_RELATIVE))
+ .contains("from-template-wf"));
+ assertFalse(
+
Files.exists(defaultHome.resolve(LocalRunConfigurationSeeder.EXECUTION_INFO_RELATIVE)));
+ }
+
+ @Test
+ void copiesNestedTemplateSubfolders() throws Exception {
+ Path hopHome = temp.resolve("hop");
+ Path template = hopHome.resolve("config/projects/default");
+ Files.createDirectories(template.resolve("metadata/rdbms"));
+ Files.createDirectories(template.resolve("datasets"));
+ Files.writeString(
+ template.resolve(HopSetupVariables.PROJECT_CONFIG_FILENAME),
+ "{ \"description\": \"template\" }");
+ Files.writeString(template.resolve("metadata/rdbms/sample.json"), "{
\"name\": \"sample\" }");
+ Files.writeString(template.resolve("datasets/customers.csv"), "id,name");
+
+ Path defaultHome = temp.resolve("Documents/hop/default");
+ HopEnvironmentSpec spec = new HopEnvironmentSpec();
+ spec.setConfigFolder(temp.resolve("user-config").toString());
+ spec.setCreateDefaultProject(true);
+ spec.setDefaultProjectHome(defaultHome.toString());
+
+ new ProjectRegistrationWriter()
+ .apply(
+ spec,
+ OsFamily.UNIX,
+ unixPaths(temp.resolve("home")),
+ new HopEnvironmentApplyResult(),
+ hopHome);
+
+ assertTrue(
+
Files.readString(defaultHome.resolve("metadata/rdbms/sample.json")).contains("sample"));
+
assertTrue(Files.readString(defaultHome.resolve("datasets/customers.csv")).contains("id,name"));
+
assertTrue(Files.exists(defaultHome.resolve(HopSetupVariables.PROJECT_CONFIG_FILENAME)));
+ }
+
@Test
void doesNotOverwriteExistingDefaultProjectConfig() throws Exception {
Path defaultHome = temp.resolve("docs/hop/default");
@@ -112,6 +190,7 @@ class ProjectRegistrationWriterTest {
assertTrue(
Files.readString(defaultHome.resolve(HopSetupVariables.PROJECT_CONFIG_FILENAME))
.contains("keep"));
+
assertFalse(Files.exists(defaultHome.resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE)));
}
@Test
@@ -130,7 +209,10 @@ class ProjectRegistrationWriterTest {
.apply(spec, OsFamily.UNIX, unixPaths(temp.resolve("home")), result,
null);
assertFalse(Files.exists(configFolder.resolve(HopSetupVariables.HOP_CONFIG_JSON)));
- assertTrue(result.describe().contains("Would"));
+ assertTrue(
+ result.describe().contains("Would add local pipeline and workflow run
configurations"));
+ assertFalse(
+
Files.exists(temp.resolve("p").resolve(LocalRunConfigurationSeeder.PIPELINE_RELATIVE)));
}
@Test