getAllActivatedGlobalRules() {
+ return get().activatedGlobalRules;
+ }
+
+ @Override
+ public GlobalConfigurationCategory getCategory() {
+ return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Unclassified.class);
+ }
+
+ @Override
+ public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+ try {
+ activatedGlobalRules.rebuildHetero(req, json, getAllGlobalRules(), "rules");
+ } catch (IOException e) {
+ throw new FormException(e, "rules");
+ }
+
+ this.save();
+
+ return true;
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterGlobalRule.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterGlobalRule.java
new file mode 100644
index 000000000000..4f1bd76110c1
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterGlobalRule.java
@@ -0,0 +1,61 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.ExtensionPoint;
+import hudson.Launcher;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import hudson.model.Run;
+import jenkins.model.Jenkins;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+import java.io.Serializable;
+
+/**
+ * Environment variables filter rule that is configured globally for all jobs.
+ * The job types can be filtered using {@link #isApplicable(Run, Object, Launcher)}
+ *
+ * The local rules are applied before the global ones.
+ *
+ * @since TODO
+ */
+@Restricted(Beta.class)
+public interface EnvVarsFilterGlobalRule extends Describable, EnvVarsFilterRule, ExtensionPoint, Serializable {
+ @SuppressWarnings("unchecked")
+ default Descriptor getDescriptor() {
+ return (Descriptor) Jenkins.get().getDescriptorOrDie(getClass());
+ }
+
+ /**
+ * @param run The executing run that has one of its step requiring environment filters
+ * @param builder Normally inherits from {@link EnvVarsFilterableBuilder} but not forced to let reflection usage in plugins
+ * @param launcher The launcher that will be used to run the command
+ * @return true iff the rule can be applied to that builder
+ */
+ boolean isApplicable(@CheckForNull Run,?> run, @NonNull Object builder, @NonNull Launcher launcher);
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRule.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRule.java
new file mode 100644
index 000000000000..eb31bab94275
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRule.java
@@ -0,0 +1,47 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import hudson.ExtensionPoint;
+import hudson.model.Describable;
+import jenkins.model.Jenkins;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+import java.io.Serializable;
+
+/**
+ * Environment variables filter rule that is specific to a job configuration, using script-specific variables, etc.
+ * The job types can be filtered using {@link EnvVarsFilterLocalRuleDescriptor#isApplicable(Class)}
+ *
+ * The local rules are applied before the global ones.
+ *
+ * @since TODO
+ */
+@Restricted(Beta.class)
+public interface EnvVarsFilterLocalRule extends Describable, EnvVarsFilterRule, ExtensionPoint, Serializable {
+ default EnvVarsFilterLocalRuleDescriptor getDescriptor() {
+ return (EnvVarsFilterLocalRuleDescriptor) Jenkins.get().getDescriptorOrDie(getClass());
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRuleDescriptor.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRuleDescriptor.java
new file mode 100644
index 000000000000..13812025831e
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterLocalRuleDescriptor.java
@@ -0,0 +1,55 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.DescriptorExtensionList;
+import hudson.model.Descriptor;
+import jenkins.model.Jenkins;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Descriptor for the local rule. Compared to the global rule, it's the descriptor that determine
+ * if the rule is applicable to a given builder and then applied every time.
+ * For global rule it's the inverse, the rule itself determines when it's applicable.
+ *
+ * @since TODO
+ */
+@Restricted(Beta.class)
+public abstract class EnvVarsFilterLocalRuleDescriptor extends Descriptor {
+ public abstract boolean isApplicable(@NonNull Class extends EnvVarsFilterableBuilder> builderClass);
+
+ public static List allApplicableFor(Class extends EnvVarsFilterableBuilder> builderClass) {
+ DescriptorExtensionList allSpecificRules =
+ Jenkins.get().getDescriptorList(EnvVarsFilterLocalRule.class);
+
+ return allSpecificRules.stream()
+ .filter(rule -> rule.isApplicable(builderClass))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRule.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRule.java
new file mode 100644
index 000000000000..384021b81d81
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRule.java
@@ -0,0 +1,57 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.EnvVars;
+import hudson.Extension;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+import java.io.Serializable;
+
+/**
+ * The order of execution of the rules is determined by first their type (local before global)
+ * and then, by default, their {@link Extension#ordinal()}, higher ordinal first, but configuration can customize the order.
+ */
+@Restricted(Beta.class)
+public interface EnvVarsFilterRule extends Serializable {
+ /**
+ * In case the filter detects something that must stop the build, it must throw a {@link EnvVarsFilterException}.
+ * This method may be executed on agents through a remoting channel.
+ */
+ void filter(@NonNull EnvVars envVars, @NonNull EnvVarsFilterRuleContext context) throws EnvVarsFilterException;
+
+ default String getDisplayName() {
+ if (this instanceof Describable>) {
+ final Descriptor> descriptor = ((Describable>) this).getDescriptor();
+ if (descriptor != null) {
+ return descriptor.getDisplayName();
+ }
+ }
+ return this.getClass().getSimpleName();
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRuleContext.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRuleContext.java
new file mode 100644
index 000000000000..325d32f3be24
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRuleContext.java
@@ -0,0 +1,54 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.Launcher;
+import hudson.model.TaskListener;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+/**
+ * Information that is used for the environment filtering process.
+ *
+ * @since TODO
+ */
+@Restricted(Beta.class)
+public class EnvVarsFilterRuleContext {
+ private final Launcher launcher;
+ private final TaskListener taskListener;
+
+ public EnvVarsFilterRuleContext(@NonNull Launcher launcher, @NonNull TaskListener taskListener) {
+ this.launcher = launcher;
+ this.taskListener = taskListener;
+ }
+
+ public Launcher getLauncher() {
+ return launcher;
+ }
+
+ public TaskListener getTaskListener() {
+ return taskListener;
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRuleWrapper.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRuleWrapper.java
new file mode 100644
index 000000000000..72d83298667c
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterRuleWrapper.java
@@ -0,0 +1,83 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.EnvVars;
+import hudson.Launcher;
+import hudson.model.Run;
+import hudson.model.TaskListener;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class that provide the list of rules (local + global) for a given builder.
+ *
+ * @since TODO
+ */
+@Restricted(NoExternalUse.class)
+public class EnvVarsFilterRuleWrapper implements Serializable {
+ private static final long serialVersionUID = -8647970104978388598L;
+ private List rules;
+
+ public EnvVarsFilterRuleWrapper(@NonNull List rules) {
+ this.rules = rules;
+ }
+
+ public static @NonNull
+ EnvVarsFilterRuleWrapper createRuleWrapper(@CheckForNull Run,?> run,
+ @NonNull Object builder,
+ @NonNull Launcher launcher,
+ @NonNull List localRules) {
+ List globalRules = EnvVarsFilterGlobalConfiguration.getAllActivatedGlobalRules();
+ List applicableGlobalRules = globalRules.stream()
+ .filter(rule -> rule.isApplicable(run, builder, launcher))
+ .collect(Collectors.toList());
+
+ List applicableRules = new ArrayList<>();
+ applicableRules.addAll(localRules);
+ applicableRules.addAll(applicableGlobalRules);
+
+ return new EnvVarsFilterRuleWrapper(applicableRules);
+ }
+
+ public void filter(@NonNull EnvVars envVars, @NonNull Launcher launcher, @NonNull TaskListener listener) throws EnvVarsFilterException {
+ EnvVarsFilterRuleContext context = new EnvVarsFilterRuleContext(launcher, listener);
+ for (EnvVarsFilterRule rule : rules) {
+ try {
+ rule.filter(envVars, context);
+ } catch (EnvVarsFilterException e) {
+ String message = String.format("Environment variable filtering failed due to violation with the message: %s", e.getMessage());
+ context.getTaskListener().error(message);
+ throw e;
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterableBuilder.java b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterableBuilder.java
new file mode 100644
index 000000000000..001ead6a8460
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/EnvVarsFilterableBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.Beta;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Builder step that wants to integrate local environment filter rules should implement this interface
+ *
+ * @since TODO
+ */
+@Restricted(Beta.class)
+public interface EnvVarsFilterableBuilder {
+ /**
+ * The order is respected for the execution. Local rules will be executed before the global ones.
+ *
+ * This method is called only once per step to create the {@link EnvVarsFilterRuleContext}.
+ *
+ * The default implementation returns an empty list; this allows build steps to support global rules.
+ */
+ @NonNull
+ default List buildEnvVarsFilterRules() {
+ return Collections.emptyList();
+ }
+}
diff --git a/core/src/main/java/jenkins/tasks/filters/impl/RetainVariablesLocalRule.java b/core/src/main/java/jenkins/tasks/filters/impl/RetainVariablesLocalRule.java
new file mode 100644
index 000000000000..c0fed8d4ea81
--- /dev/null
+++ b/core/src/main/java/jenkins/tasks/filters/impl/RetainVariablesLocalRule.java
@@ -0,0 +1,227 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package jenkins.tasks.filters.impl;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.EnvVars;
+import hudson.Extension;
+import hudson.model.Job;
+import hudson.model.Run;
+import hudson.util.FormValidation;
+import jenkins.tasks.filters.EnvVarsFilterRuleContext;
+import jenkins.tasks.filters.EnvVarsFilterLocalRule;
+import jenkins.tasks.filters.EnvVarsFilterLocalRuleDescriptor;
+import jenkins.tasks.filters.EnvVarsFilterableBuilder;
+import org.apache.commons.lang.StringUtils;
+import org.jenkinsci.Symbol;
+import org.jvnet.localizer.Localizable;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.DataBoundSetter;
+import org.kohsuke.stapler.QueryParameter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Local rule that removes all the non-retained variables for that step.
+ *
+ * @since TODO
+ */
+@Restricted(NoExternalUse.class)
+public class RetainVariablesLocalRule implements EnvVarsFilterLocalRule {
+
+ /**
+ * The variables considered to be 'characteristic' for the purposes of this rule.
+ *
+ * @see Job#getCharacteristicEnvVars()
+ * @see Run#getCharacteristicEnvVars()
+ */
+ // TODO Make the 'HUDSON_COOKIE' variable less special so we can remove it.
+ // TODO consider just querying the build, if any, for its characteristic env vars
+ private static final List CHARACTERISTIC_ENV_VARS = Arrays.asList("jenkins_server_cookie", "hudson_server_cookie", "job_name", "job_base_name", "build_number", "build_id", "build_tag");
+ /**
+ * List of lowercase names of variable that will be retained from removal
+ */
+ private String variables = "";
+ private boolean retainCharacteristicEnvVars = true;
+ private ProcessVariablesHandling processVariablesHandling = ProcessVariablesHandling.RESET;
+
+ @DataBoundConstructor
+ public RetainVariablesLocalRule() {
+ }
+
+ @DataBoundSetter
+ public void setVariables(@NonNull String variables) {
+ this.variables = variables;
+ }
+
+ private static List convertStringToList(@NonNull String variablesCommaSeparated) {
+ String[] variablesArray = variablesCommaSeparated.split("\\s+");
+ List variables = new ArrayList<>();
+ for (String nameFragment : variablesArray) {
+ if (StringUtils.isNotBlank(nameFragment)) {
+ variables.add(nameFragment.toLowerCase(Locale.ENGLISH));
+ }
+ }
+
+ Collections.sort(variables); // TODO do we really want to sort this?
+ return variables;
+ }
+
+ // for jelly view
+ @Restricted(NoExternalUse.class)
+ public @NonNull String getVariables() {
+ return variables;
+ }
+
+ @DataBoundSetter
+ public void setRetainCharacteristicEnvVars(boolean retainCharacteristicEnvVars) {
+ this.retainCharacteristicEnvVars = retainCharacteristicEnvVars;
+ }
+
+ /**
+ * Whether to retain characteristic environment variables.
+ * @return true if and only if to retain characteristic environment variables.
+ *
+ * @see Job#getCharacteristicEnvVars()
+ * @see Run#getCharacteristicEnvVars()
+ */
+ public boolean isRetainCharacteristicEnvVars() {
+ return retainCharacteristicEnvVars;
+ }
+
+ private List variablesToRetain() {
+ List vars = new ArrayList<>(convertStringToList(this.variables));
+ if (isRetainCharacteristicEnvVars()) {
+ vars.addAll(CHARACTERISTIC_ENV_VARS);
+ }
+ return vars;
+ }
+
+ @Override
+ public void filter(@NonNull EnvVars envVars, @NonNull EnvVarsFilterRuleContext context) {
+ Map systemEnvVars = EnvVars.masterEnvVars;
+
+ final List variablesRemoved = new ArrayList<>();
+ final List variablesReset = new ArrayList<>();
+ final List variables = variablesToRetain();
+ for (Iterator> iterator = envVars.entrySet().iterator(); iterator.hasNext(); ) {
+ Map.Entry entry = iterator.next();
+ String variableName = entry.getKey();
+ String variableValue = entry.getValue();
+
+ if (!variables.contains(variableName.toLowerCase(Locale.ENGLISH))) {
+ // systemEnvVars's keys are case insensitive
+ String systemValue = systemEnvVars.get(variableName);
+
+ if (systemValue == null) {
+ variablesRemoved.add(variableName);
+ iterator.remove();
+ } else {
+ switch (processVariablesHandling) {
+ case RESET:
+ if (!systemValue.equals(variableValue)) {
+ variablesReset.add(variableName);
+ }
+ break;
+ case REMOVE:
+ variablesRemoved.add(variableName);
+ iterator.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ if (!variablesRemoved.isEmpty()) {
+ context.getTaskListener().getLogger().println(Messages.RetainVariablesLocalRule_RemovalMessage(getDescriptor().getDisplayName(), StringUtils.join(variablesRemoved.toArray(), ", ")));
+ }
+ if (!variablesReset.isEmpty()) {
+ // reset the variables using the initial value from System
+ variablesReset.forEach(variableName -> envVars.put(variableName, systemEnvVars.get(variableName)));
+ context.getTaskListener().getLogger().println(Messages.RetainVariablesLocalRule_ResetMessage(getDescriptor().getDisplayName(), StringUtils.join(variablesReset.toArray(), ", ")));
+ }
+ }
+
+ public ProcessVariablesHandling getProcessVariablesHandling() {
+ return processVariablesHandling;
+ }
+
+ @DataBoundSetter
+ public void setProcessVariablesHandling(ProcessVariablesHandling processVariablesHandling) {
+ this.processVariablesHandling = processVariablesHandling;
+ }
+
+ // the ordinal is used to sort the rules in term of execution, the higher value first
+ // and take care of the fact that local rules are always applied before global ones
+ @Extension(ordinal = 1000)
+ @Symbol("retainOnlyVariables")
+ public static final class DescriptorImpl extends EnvVarsFilterLocalRuleDescriptor {
+
+ public DescriptorImpl() {
+ super();
+ load();
+ }
+
+ @Restricted(NoExternalUse.class)
+ public FormValidation doCheckRetainCharacteristicEnvVars(@QueryParameter boolean value) {
+ if (!value) {
+ return FormValidation.warning(Messages.RetainVariablesLocalRule_CharacteristicEnvVarsFormValidationWarning());
+ }
+ return FormValidation.ok(Messages.RetainVariablesLocalRule_CharacteristicEnvVarsFormValidationOK());
+ }
+
+ @Override
+ public @NonNull String getDisplayName() {
+ return Messages.RetainVariablesLocalRule_DisplayName();
+ }
+
+ @Override
+ public boolean isApplicable(@NonNull Class extends EnvVarsFilterableBuilder> builderClass) {
+ return true;
+ }
+ }
+
+ public enum ProcessVariablesHandling {
+ RESET(Messages._RetainVariablesLocalRule_RESET_DisplayName()),
+ REMOVE(Messages._RetainVariablesLocalRule_REMOVE_DisplayName());
+
+ private final Localizable localizable;
+
+ ProcessVariablesHandling(Localizable localizable) {
+ this.localizable = localizable;
+ }
+
+ public String getDisplayName() {
+ return localizable.toString();
+ }
+ }
+}
diff --git a/core/src/main/resources/hudson/tasks/BatchFile/config.jelly b/core/src/main/resources/hudson/tasks/BatchFile/config.jelly
index 19fbdcef646d..4918b5501270 100644
--- a/core/src/main/resources/hudson/tasks/BatchFile/config.jelly
+++ b/core/src/main/resources/hudson/tasks/BatchFile/config.jelly
@@ -32,5 +32,17 @@ THE SOFTWARE.
+
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/tasks/BatchFile/config.properties b/core/src/main/resources/hudson/tasks/BatchFile/config.properties
index e416300f208f..d88d5ac12011 100644
--- a/core/src/main/resources/hudson/tasks/BatchFile/config.properties
+++ b/core/src/main/resources/hudson/tasks/BatchFile/config.properties
@@ -21,3 +21,5 @@
# THE SOFTWARE.
description=See the list of available environment variables
+filterRules=Environment filters
+addFilterRule=Add environment filter
diff --git a/core/src/main/resources/hudson/tasks/Shell/config.groovy b/core/src/main/resources/hudson/tasks/Shell/config.groovy
index 811ca08d1d72..f895ffe88f3e 100644
--- a/core/src/main/resources/hudson/tasks/Shell/config.groovy
+++ b/core/src/main/resources/hudson/tasks/Shell/config.groovy
@@ -33,4 +33,17 @@ f.advanced() {
f.number(clazz:"positive-number", value: instance?.unstableReturn, min:1, max:255, step:1)
}
+ if (instance?.configuredLocalRules || descriptor.applicableLocalRules) {
+ f.entry(title: _("filterRules")) {
+ f.hetero_list(
+ name: "configuredLocalRules",
+ hasHeader: true,
+ oneEach: true,
+ disableDragAndDrop: true,
+ descriptors: descriptor.applicableLocalRules,
+ items: instance?.configuredLocalRules,
+ addCaption: _("addFilterRule")
+ )
+ }
+ }
}
diff --git a/core/src/main/resources/hudson/tasks/Shell/config.properties b/core/src/main/resources/hudson/tasks/Shell/config.properties
index 0d7b36f4a5a1..5a69c4dc7c2e 100644
--- a/core/src/main/resources/hudson/tasks/Shell/config.properties
+++ b/core/src/main/resources/hudson/tasks/Shell/config.properties
@@ -21,3 +21,5 @@
# THE SOFTWARE.
description=See the list of available environment variables
+filterRules=Environment filters
+addFilterRule=Add environment filter
diff --git a/core/src/main/resources/jenkins/tasks/filters/EnvVarsFilterGlobalConfiguration/config.jelly b/core/src/main/resources/jenkins/tasks/filters/EnvVarsFilterGlobalConfiguration/config.jelly
new file mode 100644
index 000000000000..04af1021f5b1
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/EnvVarsFilterGlobalConfiguration/config.jelly
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/main/resources/jenkins/tasks/filters/EnvVarsFilterGlobalConfiguration/config.properties b/core/src/main/resources/jenkins/tasks/filters/EnvVarsFilterGlobalConfiguration/config.properties
new file mode 100644
index 000000000000..5fc3224a81a8
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/EnvVarsFilterGlobalConfiguration/config.properties
@@ -0,0 +1,25 @@
+# The MIT License
+#
+# Copyright (c) 2020, CloudBees, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+jobEnvironmentVariableFilters=Filter build step environment variables
+globalRules=Global filter rules
+showGlobalRules=Show global filter rules
+addRule=Add filter rule
diff --git a/core/src/main/resources/jenkins/tasks/filters/impl/Messages.properties b/core/src/main/resources/jenkins/tasks/filters/impl/Messages.properties
new file mode 100644
index 000000000000..b022f01cfe70
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/impl/Messages.properties
@@ -0,0 +1,29 @@
+# The MIT License
+#
+# Copyright (c) 2020, CloudBees, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+RetainVariablesLocalRule.DisplayName=Only Keep Specified Environment Variables
+RetainVariablesLocalRule_RESET_DisplayName=Reset to default value
+RetainVariablesLocalRule_REMOVE_DisplayName=Remove from environment
+RetainVariablesLocalRule.RemovalMessage=The following environment variables were removed by ''{0}'': {1}
+RetainVariablesLocalRule.ResetMessage=The following environment variables were reset to their system default value by ''{0}'': {1}
+RetainVariablesLocalRule.CharacteristicEnvVarsFormValidationWarning=It is recommended to retain characteristic environment variables, because Jenkins uses them to identify and kill runaway processes after a build is finished.
+RetainVariablesLocalRule.CharacteristicEnvVarsFormValidationOK=In addition to any environment variables listed above, Jenkins will also retain environment variables it needs to identify and kill runaway processes when the build is done.
diff --git a/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/config.jelly b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/config.jelly
new file mode 100644
index 000000000000..76a418690f8d
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/config.jelly
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ ${it.displayName}
+
+
+
diff --git a/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/config.properties b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/config.properties
new file mode 100644
index 000000000000..430c740d6484
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/config.properties
@@ -0,0 +1,26 @@
+# The MIT License
+#
+# Copyright (c) 2020, CloudBees, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+variablesLabel=Environment variables to retain
+variablesDescription=All other environment variables provided by Jenkins will be removed. \
+ Environment variables defined outside Jenkins will be reset to their default value or removed, depending on the option ''Process environment variables handling'', unless specified here.
+retainCharacteristicEnvVarsLabel=Retain characteristic environment variables
+retainProcessVariablesLabel=Process environment variables handling
diff --git a/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help-retainCharacteristicEnvVars.html b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help-retainCharacteristicEnvVars.html
new file mode 100644
index 000000000000..774d44c499f0
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help-retainCharacteristicEnvVars.html
@@ -0,0 +1,5 @@
+
+ When checked, characteristic environment variables will be retained in addition to the variables listed above.
+ These environment variables are job- and build-specific, defined by Jenkins, and are used to identify and kill processes started by this build step.
+ See the documentation for more details on starting processes.
+
diff --git a/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help-variables.html b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help-variables.html
new file mode 100644
index 000000000000..0788d14647d3
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help-variables.html
@@ -0,0 +1,3 @@
+
+
Whitespace separated, case insensitive list of environment variables that will be retained, i.e. not removed from the environment of this build step or reset to their default.
+
diff --git a/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help.html b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help.html
new file mode 100644
index 000000000000..2a4c6fb57117
--- /dev/null
+++ b/core/src/main/resources/jenkins/tasks/filters/impl/RetainVariablesLocalRule/help.html
@@ -0,0 +1,48 @@
+
+
Limit which environment variables are passed to a build step.
+
+
Environment variables passed to the build step are filtered, unless listed below.
+
+
The behavior of this filter depends on whether the environment variable is originally defined outside Jenkins:
+
+ - If the environment variable originates from Jenkins configuration, such as
JOB_URL,
+ it will not be passed to the build step unless specified here.
+ - If the environment variable originates from outside Jenkins, such as
PATH,
+ the behavior depends on the option Process environment variables handling:
+ If that option is set to Retain, the original value will be passed to the build step, discarding any modifications inside Jenkins.
+ If that option is set to Remove, the variable will not be passed to the build step.
+
+
+
+ The following table shows the effect of filtering on an environment variable:
+
+
+
+ | Behavior |
+ Originally defined outside Jenkins |
+ Originally defined inside Jenkins |
+
+
+ |
+ Process environment variables handling: reset
+ |
+
+ Variable is reset to original value
+ |
+
+ Variable is removed
+ |
+
+
+ |
+ Process environment variables handling: removed
+ |
+
+ Variable is removed
+ |
+
+ Variable is removed
+ |
+
+
+
diff --git a/core/src/main/resources/lib/form/hetero-list.jelly b/core/src/main/resources/lib/form/hetero-list.jelly
index 973fdd4cb4a9..bf26d1414333 100644
--- a/core/src/main/resources/lib/form/hetero-list.jelly
+++ b/core/src/main/resources/lib/form/hetero-list.jelly
@@ -73,6 +73,10 @@ THE SOFTWARE.
If set to an item of the form className#classMethod, it will be used
to call className.classMethod(descriptor) to calculate each item title.
+
+ If true the drag and drop will not be activated. This just removes the drag and
+ drop UI, it will not prevent users from manually submitting a different order.
+
@@ -81,7 +85,7 @@ THE SOFTWARE.
-
+
${descriptor.displayName}
|
@@ -112,11 +116,11 @@ THE SOFTWARE.