diff --git a/src/main/java/testingbot/TunnelManager.java b/src/main/java/testingbot/TunnelManager.java index 169b7f4..ce5d63f 100644 --- a/src/main/java/testingbot/TunnelManager.java +++ b/src/main/java/testingbot/TunnelManager.java @@ -148,6 +148,58 @@ private static boolean hasValue(String[] tokens, int index) { return index + 1 < tokens.length && !tokens[index + 1].startsWith("-"); } + /** + * Classifies {@code rawOptions} and returns the tokens that {@link #applyOptions} would ignore or + * reject (unknown flags, flags missing a required value, an invalid {@code --metrics-port}). Used + * for config-time form validation so mistakes surface before a build runs. Pure — nothing is + * applied. Keep the recognized-flag set in sync with {@link #applyOptions}. + */ + public static List unsupportedOptions(String rawOptions) { + List problems = new ArrayList<>(); + String trimmed = rawOptions == null ? "" : rawOptions.trim(); + if (trimmed.isEmpty()) { + return problems; + } + String[] tokens = tokenize(trimmed); + for (int i = 0; i < tokens.length; i++) { + String token = tokens[i]; + switch (token) { + case "-d": + case "--debug": + break; + case "-i": + case "--tunnel-identifier": + case "-Y": + case "--proxy": + case "-z": + case "--proxy-userpwd": + case "-a": + case "--auth": + if (hasValue(tokens, i)) { + i++; + } else { + problems.add(token + " (missing value)"); + } + break; + case "--metrics-port": + if (hasValue(tokens, i)) { + String value = tokens[++i]; + try { + Integer.parseInt(value); + } catch (NumberFormatException nfe) { + problems.add(token + " " + value + " (invalid value)"); + } + } else { + problems.add(token + " (missing value)"); + } + break; + default: + problems.add(token); + } + } + return problems; + } + /** * Returns the value of a user-supplied {@code --tunnel-identifier} / {@code -i} option, or * {@code null} if the options don't specify one. diff --git a/src/main/java/testingbot/pipeline/TestingBotTunnelStep.java b/src/main/java/testingbot/pipeline/TestingBotTunnelStep.java index b5b7c19..7a993f8 100644 --- a/src/main/java/testingbot/pipeline/TestingBotTunnelStep.java +++ b/src/main/java/testingbot/pipeline/TestingBotTunnelStep.java @@ -16,9 +16,11 @@ import hudson.model.TopLevelItem; import hudson.remoting.VirtualChannel; import hudson.security.ACL; +import hudson.util.FormValidation; import hudson.util.ListBoxModel; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Set; import jenkins.model.Jenkins; import org.jenkinsci.plugins.workflow.steps.BodyExecution; @@ -32,6 +34,7 @@ import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.verb.POST; import testingbot.TestingBotBuildAction; import testingbot.TestingBotBuildReportAction; @@ -110,6 +113,16 @@ public ListBoxModel doFillCredentialsIdItems(final @AncestorInPath Item context) new ArrayList() )); } + + public FormValidation doCheckOptions(@QueryParameter String value) { + List problems = TunnelManager.unsupportedOptions(value); + if (problems.isEmpty()) { + return FormValidation.ok(); + } + // A warning, not an error: unsupported options are ignored at runtime, they don't block the build. + return FormValidation.warning( + "These tunnel option(s) are not recognized and will be ignored: " + String.join(", ", problems)); + } } public static class TestingBotTunnelStepExecution extends StepExecution {