This is an automated email from the ASF dual-hosted git repository.
hansva 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 d6ddcca717 issue #7656: load old lifecycle environments without
marketplace env errors (#7657)
d6ddcca717 is described below
commit d6ddcca7176c714645bd16e619fc5495545306af
Author: Matt Casters <[email protected]>
AuthorDate: Tue Jul 28 11:37:11 2026 +0200
issue #7656: load old lifecycle environments without marketplace env errors
(#7657)
* issue #7656: do not fail enabling environments without marketplace env
Old lifecycle environments have no marketplace attributes. Runtime now
treats missing onEnable as off, and missing envFile as a silent skip so
project switches no longer error when hop-env.yaml is absent.
* issue #7656: never block Hop GUI when enabling a lifecycle environment
Marketplace onEnable checks only warn in the GUI (including enforce).
Any failure is caught so project/environment open always succeeds;
hard-fail remains for non-GUI runtimes only.
---
.../hop/marketplace/env/MarketplaceAttributes.java | 29 ++-
...fecycleEnvironmentDialogTabsExtensionPoint.java | 10 +-
...ojectEnvironmentAfterEnabledExtensionPoint.java | 197 +++++++++++++++------
.../gui/messages/messages_en_US.properties | 2 +-
.../marketplace/env/MarketplaceAttributesTest.java | 26 ++-
5 files changed, 193 insertions(+), 71 deletions(-)
diff --git
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/env/MarketplaceAttributes.java
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/env/MarketplaceAttributes.java
index faef714c38..c1cb856d93 100644
---
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/env/MarketplaceAttributes.java
+++
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/env/MarketplaceAttributes.java
@@ -51,22 +51,32 @@ public final class MarketplaceAttributes {
private MarketplaceAttributes() {}
/**
- * Resolve on-enable policy from attributes, falling back to purpose-based
defaults when unset.
+ * Resolve on-enable policy from attributes for <strong>runtime</strong>
checks.
*
- * <ul>
- * <li>Production → enforce
- * <li>Testing / Acceptance → warn
- * <li>Development / CI / CB / other → off
- * </ul>
+ * <p>When {@link #KEY_ON_ENABLE} is unset (typical of pre-marketplace
lifecycle environments),
+ * returns {@link #ON_ENABLE_OFF} so enabling a project/environment never
fails solely because the
+ * marketplace plugin is present. Purpose-based suggestions are only for the
UI via {@link
+ * #defaultOnEnableForPurpose(String)}.
*/
public static String resolveOnEnable(IAttributes attributes, String purpose)
{
String explicit = attributes != null ? attributes.getAttribute(GROUP,
KEY_ON_ENABLE) : null;
if (StringUtils.isNotBlank(explicit)) {
return explicit.trim().toLowerCase();
}
- return defaultOnEnableForPurpose(purpose);
+ // Missing attribute → opt-out (issue #7656: old hop configs must load
silently)
+ return ON_ENABLE_OFF;
}
+ /**
+ * Suggested on-enable value when the user has not saved a marketplace
choice yet (dialog defaults
+ * only — not used for runtime enforcement).
+ *
+ * <ul>
+ * <li>Production → enforce
+ * <li>Testing / Acceptance → warn
+ * <li>Development / CI / CB / other → off
+ * </ul>
+ */
public static String defaultOnEnableForPurpose(String purpose) {
if (StringUtils.isBlank(purpose)) {
return ON_ENABLE_OFF;
@@ -81,6 +91,11 @@ public final class MarketplaceAttributes {
return ON_ENABLE_OFF;
}
+ /** True when the lifecycle environment has an explicit marketplace envFile
attribute. */
+ public static boolean hasExplicitEnvFile(IAttributes attributes) {
+ return StringUtils.isNotBlank(envFile(attributes));
+ }
+
public static boolean isStrict(IAttributes attributes) {
if (attributes == null) {
return false;
diff --git
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/LifecycleEnvironmentDialogTabsExtensionPoint.java
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/LifecycleEnvironmentDialogTabsExtensionPoint.java
index 95590eea6a..b5eb8e22d2 100644
---
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/LifecycleEnvironmentDialogTabsExtensionPoint.java
+++
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/LifecycleEnvironmentDialogTabsExtensionPoint.java
@@ -214,8 +214,14 @@ public class LifecycleEnvironmentDialogTabsExtensionPoint
return;
}
wEnvFile.setText(Const.NVL(MarketplaceAttributes.envFile(context), ""));
- // Explicit attribute, or purpose-based default when unset (not written
until save).
- wOnEnable.setText(MarketplaceAttributes.resolveOnEnable(context,
context.getPurpose()));
+ // Runtime uses OFF when unset; dialog suggests a purpose-based default
until the user saves.
+ String storedOnEnable =
+ context.getAttribute(MarketplaceAttributes.GROUP,
MarketplaceAttributes.KEY_ON_ENABLE);
+ if (StringUtils.isNotBlank(storedOnEnable)) {
+ wOnEnable.setText(storedOnEnable.trim());
+ } else {
+
wOnEnable.setText(MarketplaceAttributes.defaultOnEnableForPurpose(context.getPurpose()));
+ }
wStrict.setSelection(MarketplaceAttributes.isStrict(context));
wAutoApply.setSelection(MarketplaceAttributes.isAutoApply(context));
}
diff --git
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/ProjectEnvironmentAfterEnabledExtensionPoint.java
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/ProjectEnvironmentAfterEnabledExtensionPoint.java
index f12002b983..48754a9478 100644
---
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/ProjectEnvironmentAfterEnabledExtensionPoint.java
+++
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/xp/ProjectEnvironmentAfterEnabledExtensionPoint.java
@@ -44,8 +44,14 @@ import org.eclipse.swt.SWT;
/**
* When a project lifecycle environment is enabled, optionally validate (or
apply) the marketplace
- * hop-env file against the local install. Settings come from {@link
AttributesContext} group {@link
- * MarketplaceAttributes#GROUP} — no dependency on the Projects plugin classes.
+ * hop-env file against the local install.
+ *
+ * <p><strong>Hop GUI:</strong> this extension <em>never</em> throws. Missing
config, missing
+ * hop-env, parse errors, and drift only log (and optionally show a warning
dialog). Blocking enable
+ * would trap users out of their environment with no GUI recovery path (issue
#7656).
+ *
+ * <p><strong>Non-GUI:</strong> {@code onEnable=enforce} may still throw so
automation can hard-fail
+ * (prefer {@code hop marketplace validate} / hop-run checks for CI).
*/
@ExtensionPoint(
id = "MarketplaceProjectEnvironmentAfterEnabled",
@@ -58,16 +64,45 @@ public class ProjectEnvironmentAfterEnabledExtensionPoint
@Override
public void callExtensionPoint(ILogChannel log, IVariables variables,
AttributesContext context)
throws HopException {
+ // Outer guard: never let marketplace break environment enable in the GUI.
+ try {
+ runCheck(log, variables, context);
+ } catch (Exception e) {
+ if (isGuiRuntime()) {
+ if (log != null) {
+ log.logError(
+ "Marketplace environment check failed but environment enable
continues: "
+ + e.getMessage(),
+ e);
+ }
+ warnGui(
+ "Marketplace check failed",
+ "The marketplace could not validate hop-env for this
environment.\n\n"
+ + Const.NVL(e.getMessage(), e.getClass().getSimpleName())
+ + "\n\nThe environment was still enabled.");
+ return;
+ }
+ if (e instanceof HopException he) {
+ throw he;
+ }
+ throw new HopException("Marketplace environment check failed", e);
+ }
+ }
+
+ private void runCheck(ILogChannel log, IVariables variables,
AttributesContext context)
+ throws Exception {
if (context == null) {
return;
}
String onEnable = MarketplaceAttributes.resolveOnEnable(context,
context.getPurpose());
if (MarketplaceAttributes.ON_ENABLE_OFF.equals(onEnable)) {
- log.logDetailed(
- "Marketplace environment check skipped (onEnable=off) for
environment '"
- + Const.NVL(context.getEnvironmentName(), "")
- + "'");
+ if (log != null) {
+ log.logDetailed(
+ "Marketplace environment check skipped (onEnable=off) for
environment '"
+ + Const.NVL(context.getEnvironmentName(), "")
+ + "'");
+ }
return;
}
@@ -75,83 +110,133 @@ public class ProjectEnvironmentAfterEnabledExtensionPoint
try {
hopHome = HopHome.resolve();
} catch (HopException e) {
- log.logDetailed("Marketplace environment check skipped: Hop home not
resolved");
+ if (log != null) {
+ log.logDetailed("Marketplace environment check skipped: Hop home not
resolved");
+ }
return;
}
+ boolean explicitEnvFile =
MarketplaceAttributes.hasExplicitEnvFile(context);
Path envFile = resolveEnvFile(context, variables, hopHome);
if (envFile == null) {
+ // No hop-env configured and none found: silent skip (issue #7656).
+ if (!explicitEnvFile) {
+ if (log != null) {
+ log.logDetailed(
+ "Marketplace environment check skipped (no envFile attribute and
no hop-env.yaml) for '"
+ + Const.NVL(context.getEnvironmentName(), "")
+ + "'");
+ }
+ return;
+ }
String msg =
"Marketplace environment file not found for environment '"
+ Const.NVL(context.getEnvironmentName(), "")
- + "'. Set marketplace attribute envFile or place hop-env.yaml
under the project home.";
- if (MarketplaceAttributes.ON_ENABLE_ENFORCE.equals(onEnable)) {
+ + "': "
+ + Const.NVL(MarketplaceAttributes.envFile(context), "")
+ + ". Fix marketplace attribute envFile or place hop-env.yaml
under the project home.";
+ // GUI: never block open — warn only. Non-GUI enforce may throw.
+ if (MarketplaceAttributes.ON_ENABLE_ENFORCE.equals(onEnable) &&
!isGuiRuntime()) {
throw new HopException(msg);
}
- log.logBasic(msg);
+ if (log != null) {
+ log.logBasic(msg);
+ }
+ if (isGuiRuntime()) {
+ warnGui("Marketplace environment file missing", msg);
+ }
return;
}
- try {
- MarketplaceConfig config = MarketplaceConfig.load();
- HopEnvironmentSpec env = HopEnvironmentLoader.load(envFile);
- EnvironmentApplier applier = new EnvironmentApplier(log, hopHome,
config);
- EnvironmentDrift drift = applier.validate(env);
+ MarketplaceConfig config = MarketplaceConfig.load();
+ HopEnvironmentSpec env = HopEnvironmentLoader.load(envFile);
+ EnvironmentApplier applier = new EnvironmentApplier(log, hopHome, config);
+ EnvironmentDrift drift = applier.validate(env);
- if (MarketplaceAttributes.isStrict(context)) {
- populateExtraPlugins(hopHome, env, drift);
- }
+ if (MarketplaceAttributes.isStrict(context)) {
+ populateExtraPlugins(hopHome, env, drift);
+ }
- boolean hard =
- !drift.getMissingPlugins().isEmpty()
- || !drift.getVersionMismatches().isEmpty()
- || !drift.getMissingDependencies().isEmpty()
- || (MarketplaceAttributes.isStrict(context)
- && !drift.getExtraMarketplacePlugins().isEmpty());
+ boolean hard =
+ !drift.getMissingPlugins().isEmpty()
+ || !drift.getVersionMismatches().isEmpty()
+ || !drift.getMissingDependencies().isEmpty()
+ || (MarketplaceAttributes.isStrict(context)
+ && !drift.getExtraMarketplacePlugins().isEmpty());
- if (!hard) {
+ if (!hard) {
+ if (log != null) {
log.logBasic("Marketplace environment file " + envFile + " matches
local install.");
- return;
}
+ return;
+ }
- String report =
- "Environment drift for '"
- + Const.NVL(context.getEnvironmentName(), "")
- + "' against "
- + envFile
- + ":\n"
- + drift.formatReport()
- + "Run 'hop marketplace apply -f "
- + envFile
- + "' to fix your environment.";
-
- if (MarketplaceAttributes.isAutoApply(context) && config.isEnabled()) {
+ String report =
+ "Environment drift for '"
+ + Const.NVL(context.getEnvironmentName(), "")
+ + "' against "
+ + envFile
+ + ":\n"
+ + drift.formatReport()
+ + "Run 'hop marketplace apply -f "
+ + envFile
+ + "' to fix your environment.";
+
+ if (MarketplaceAttributes.isAutoApply(context) && config.isEnabled()) {
+ if (log != null) {
log.logBasic("Auto-applying marketplace environment file " + envFile);
+ }
+ try {
applier.apply(env, false);
- return;
+ } catch (Exception applyEx) {
+ // Auto-apply must not strand the GUI without an open environment.
+ if (isGuiRuntime()) {
+ if (log != null) {
+ log.logError("Marketplace auto-apply failed; environment remains
enabled", applyEx);
+ }
+ warnGui(
+ "Marketplace auto-apply failed",
+ report
+ + "\n\nAuto-apply error: "
+ + Const.NVL(applyEx.getMessage(),
applyEx.getClass().getSimpleName())
+ + "\n\nThe environment was still enabled.");
+ return;
+ }
+ throw applyEx;
}
+ return;
+ }
- if (MarketplaceAttributes.ON_ENABLE_ENFORCE.equals(onEnable)) {
- throw new HopException("FATAL: " + report);
- }
+ if (MarketplaceAttributes.ON_ENABLE_ENFORCE.equals(onEnable) &&
!isGuiRuntime()) {
+ throw new HopException("FATAL: " + report);
+ }
- // warn
+ // warn (and in GUI always warn-only, even if onEnable was enforce)
+ if (log != null) {
log.logError(report);
- if ("GUI".equalsIgnoreCase(Const.getHopPlatformRuntime())) {
- try {
- MessageBox box =
- new MessageBox(HopGui.getInstance().getShell(), SWT.OK |
SWT.ICON_WARNING);
- box.setText("Marketplace environment drift");
- box.setMessage(report);
- box.open();
- } catch (Exception e) {
- // headless or shell unavailable
- }
+ if (MarketplaceAttributes.ON_ENABLE_ENFORCE.equals(onEnable) &&
isGuiRuntime()) {
+ log.logBasic(
+ "Marketplace onEnable=enforce is treated as warn in Hop GUI so
environments can always"
+ + " be opened. Use hop-run / marketplace validate for hard
fail.");
}
- } catch (HopException e) {
- throw e;
+ }
+ if (isGuiRuntime()) {
+ warnGui("Marketplace environment drift", report);
+ }
+ }
+
+ static boolean isGuiRuntime() {
+ return "GUI".equalsIgnoreCase(Const.getHopPlatformRuntime());
+ }
+
+ private static void warnGui(String title, String message) {
+ try {
+ MessageBox box = new MessageBox(HopGui.getInstance().getShell(), SWT.OK
| SWT.ICON_WARNING);
+ box.setText(title);
+ box.setMessage(message);
+ box.open();
} catch (Exception e) {
- throw new HopException("Failed to validate marketplace environment file
" + envFile, e);
+ // headless or shell unavailable
}
}
diff --git
a/plugins/misc/marketplace/src/main/resources/org/apache/hop/marketplace/gui/messages/messages_en_US.properties
b/plugins/misc/marketplace/src/main/resources/org/apache/hop/marketplace/gui/messages/messages_en_US.properties
index fbfe263eb5..ed67006ff2 100644
---
a/plugins/misc/marketplace/src/main/resources/org/apache/hop/marketplace/gui/messages/messages_en_US.properties
+++
b/plugins/misc/marketplace/src/main/resources/org/apache/hop/marketplace/gui/messages/messages_en_US.properties
@@ -24,7 +24,7 @@ MarketplaceLifecycleEnv.EnvFile.Label=Environment file
MarketplaceLifecycleEnv.Button.Browse=Browse…
MarketplaceLifecycleEnv.Button.EditEnv=Edit…
MarketplaceLifecycleEnv.OnEnable.Label=On enable
-MarketplaceLifecycleEnv.OnEnable.Tooltip=off: no check; warn: log/dialog on
drift; enforce: fail enabling the environment on drift
+MarketplaceLifecycleEnv.OnEnable.Tooltip=off: no check; warn: log/dialog on
drift; enforce: hard-fail only outside Hop GUI (hop-run / automation). In Hop
GUI, enforce is treated as warn so environments always open.
MarketplaceLifecycleEnv.Strict.Label=Strict (fail if extra marketplace plugins
installed)
MarketplaceLifecycleEnv.Strict.Tooltip=When validating, also treat marketplace
plugins with receipts that are not listed in the env file as drift
MarketplaceLifecycleEnv.AutoApply.Label=Auto-apply missing plugins on enable
diff --git
a/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/env/MarketplaceAttributesTest.java
b/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/env/MarketplaceAttributesTest.java
index a7e28d0e56..0886f13121 100644
---
a/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/env/MarketplaceAttributesTest.java
+++
b/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/env/MarketplaceAttributesTest.java
@@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test;
class MarketplaceAttributesTest {
@Test
- void purposeDefaults() {
+ void purposeDefaultsForUiOnly() {
assertEquals(
MarketplaceAttributes.ON_ENABLE_ENFORCE,
MarketplaceAttributes.defaultOnEnableForPurpose("Production"));
@@ -45,15 +45,31 @@ class MarketplaceAttributesTest {
}
@Test
- void explicitOverridesPurpose() {
+ void unsetOnEnableIsOffAtRuntimeEvenForProduction() {
+ // Old hop configs have no marketplace attributes — must not enforce
(issue #7656)
+ AttributesContext ctx = new AttributesContext();
+ assertEquals(
+ MarketplaceAttributes.ON_ENABLE_OFF,
+ MarketplaceAttributes.resolveOnEnable(ctx, "Production"));
+ assertEquals(
+ MarketplaceAttributes.ON_ENABLE_OFF,
+ MarketplaceAttributes.resolveOnEnable(null, "Production"));
+ assertFalse(MarketplaceAttributes.hasExplicitEnvFile(ctx));
+ }
+
+ @Test
+ void explicitOverridesUnsetDefault() {
AttributesContext ctx = new AttributesContext();
ctx.setAttribute(
MarketplaceAttributes.GROUP,
MarketplaceAttributes.KEY_ON_ENABLE,
- MarketplaceAttributes.ON_ENABLE_OFF);
+ MarketplaceAttributes.ON_ENABLE_ENFORCE);
assertEquals(
- MarketplaceAttributes.ON_ENABLE_OFF,
- MarketplaceAttributes.resolveOnEnable(ctx, "Production"));
+ MarketplaceAttributes.ON_ENABLE_ENFORCE,
+ MarketplaceAttributes.resolveOnEnable(ctx, "Development"));
+ ctx.setAttribute(
+ MarketplaceAttributes.GROUP, MarketplaceAttributes.KEY_ENV_FILE,
"/tmp/hop-env.yaml");
+ assertTrue(MarketplaceAttributes.hasExplicitEnvFile(ctx));
}
@Test