-
Notifications
You must be signed in to change notification settings - Fork 60
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
JENKINS-70066: Fix null check of parameters #134
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,12 @@ | |
import java.io.IOException; | ||
import java.io.Serial; | ||
import java.io.Serializable; | ||
import java.util.*; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
@@ -91,19 +96,19 @@ | |
@CheckForNull String builderId, | ||
@CheckForNull String scriptId, | ||
boolean propagateParams, | ||
Parameter[] parameters) { | ||
this(builderId, scriptId, propagateParams, Arrays.asList(Objects.requireNonNull(parameters))); | ||
@CheckForNull Parameter[] parameters) { | ||
this(builderId, scriptId, propagateParams, parameters == null ? List.of() : List.of(parameters)); | ||
} | ||
|
||
@DataBoundConstructor | ||
public ScriptlerBuilder( | ||
@CheckForNull String builderId, | ||
@CheckForNull String scriptId, | ||
boolean propagateParams, | ||
@NonNull List<Parameter> parameters) { | ||
@CheckForNull List<Parameter> parameters) { | ||
this.builderId = builderId; | ||
this.scriptId = scriptId; | ||
this.parameters = new ArrayList<>(parameters); | ||
this.parameters = parameters == null ? List.of() : List.copyOf(parameters); | ||
this.propagateParams = propagateParams; | ||
} | ||
|
||
|
@@ -175,7 +180,7 @@ | |
|
||
@NonNull | ||
public List<Parameter> getParametersList() { | ||
return Collections.unmodifiableList(parameters); | ||
return parameters; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whether you should revert this or not would depend on whether or not you want callers to modify the list. Probably you do not, so this should be reverted. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is currently safe as returning an unmodifiable view of an immutable list is a no-op. |
||
} | ||
|
||
public String getBuilderId() { | ||
|
@@ -233,155 +238,154 @@ | |
|
||
// expand the parameters before passing these to the execution, this is to allow any token macro to resolve | ||
// parameter values | ||
List<Parameter> expandedParams = new LinkedList<>(); | ||
List<Parameter> expandedParams = new ArrayList<>(); | ||
|
||
if (propagateParams) { | ||
final ParametersAction paramsAction = build.getAction(ParametersAction.class); | ||
if (paramsAction == null) { | ||
listener.getLogger().println(Messages.no_parameters_defined()); | ||
} else { | ||
final List<ParameterValue> jobParams = paramsAction.getParameters(); | ||
for (ParameterValue parameterValue : jobParams) { | ||
// pass the params to the token expander in a way that these get expanded by environment | ||
// variables (params are also environment variables) | ||
String macro = "${" + parameterValue.getName() + "}"; | ||
String value = TokenMacro.expandAll(build, listener, macro, false, null); | ||
expandedParams.add(new Parameter(parameterValue.getName(), value)); | ||
} | ||
} | ||
} | ||
for (Parameter parameter : parameters) { | ||
expandedParams.add(new Parameter( | ||
parameter.getName(), TokenMacro.expandAll(build, listener, parameter.getValue()))); | ||
} | ||
final Object output; | ||
if (script.onlyBuiltIn) { | ||
// When run on the built-in node, make build, launcher, listener available to script | ||
output = FilePath.localChannel.call(new ControllerGroovyScript( | ||
script.getScriptText(), expandedParams, true, listener, launcher, build)); | ||
} else { | ||
VirtualChannel channel = launcher.getChannel(); | ||
if (channel == null) { | ||
output = null; | ||
listener.getLogger() | ||
.println(Messages.scriptExecutionFailed(scriptId) + " - " + Messages.agent_no_channel()); | ||
} else { | ||
output = channel.call(new GroovyScript(script.getScriptText(), expandedParams, true, listener)); | ||
} | ||
} | ||
isOk = !Boolean.FALSE.equals(output); | ||
} catch (InterruptedException e) { | ||
listener.getLogger().println(Messages.scriptExecutionFailed(scriptId) + " - " + e.getMessage()); | ||
e.printStackTrace(listener.getLogger()); | ||
Thread.currentThread().interrupt(); | ||
} catch (IOException | MacroEvaluationException e) { | ||
listener.getLogger().println(Messages.scriptExecutionFailed(scriptId) + " - " + e.getMessage()); | ||
e.printStackTrace(listener.getLogger()); | ||
} | ||
|
||
return isOk; | ||
} | ||
|
||
private static String generateBuilderId() { | ||
return System.currentTimeMillis() + "_" + CURRENT_ID.addAndGet(1); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
|
||
ScriptlerBuilder that = (ScriptlerBuilder) o; | ||
|
||
return Objects.equals(propagateParams, that.propagateParams) | ||
&& Objects.equals(builderId, that.builderId) | ||
&& Objects.equals(scriptId, that.scriptId) | ||
&& Objects.equals(parameters, that.parameters); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(propagateParams, builderId, scriptId, parameters); | ||
} | ||
|
||
// Overridden for better type safety. | ||
@Override | ||
public DescriptorImpl getDescriptor() { | ||
return (DescriptorImpl) super.getDescriptor(); | ||
} | ||
|
||
/** | ||
* Automatically registered by XStream2.AssociatedConverterImpl#findConverter(Class) | ||
* Process the class regularly but add a check after that | ||
*/ | ||
@SuppressWarnings("unused") // discovered dynamically | ||
public static final class ConverterImpl extends XStream2.PassthruConverter<ScriptlerBuilder> { | ||
public ConverterImpl(XStream2 xstream) { | ||
super(xstream); | ||
} | ||
|
||
@Override | ||
protected void callback(ScriptlerBuilder obj, UnmarshallingContext context) { | ||
Map<String, String> errors = obj.checkGenericData(); | ||
|
||
if (!errors.isEmpty()) { | ||
ConversionException conversionException = new ConversionException("Validation failed"); | ||
errors.forEach(conversionException::add); | ||
throw new CriticalXStreamException(conversionException); | ||
} | ||
} | ||
} | ||
|
||
@Extension | ||
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> { | ||
|
||
@Override | ||
public boolean isApplicable(Class<? extends AbstractProject> jobType) { | ||
return Jenkins.get().hasPermission(ScriptlerPermissions.RUN_SCRIPTS); | ||
} | ||
|
||
@NonNull | ||
@Override | ||
public String getDisplayName() { | ||
return Messages.builder_name(); | ||
} | ||
|
||
// used by Jelly views | ||
public Permission getRequiredPermission() { | ||
return ScriptlerPermissions.RUN_SCRIPTS; | ||
} | ||
|
||
@Override | ||
public ScriptlerBuilder newInstance(StaplerRequest2 req, JSONObject formData) { | ||
ScriptlerBuilder builder = null; | ||
String builderId = formData.optString(BUILDER_ID); | ||
String id = formData.optString("scriptlerScriptId"); | ||
|
||
if (id != null && !id.isBlank()) { | ||
boolean inPropagateParams = formData.getBoolean("propagateParams"); | ||
List<Parameter> params = UIHelper.extractParameters(formData); | ||
builder = new ScriptlerBuilder(builderId, id, inPropagateParams, params); | ||
} | ||
|
||
if (builder != null) { | ||
Map<String, String> errors = builder.checkGenericData(); | ||
if (!errors.isEmpty()) { | ||
throw new MultipleErrorFormValidation(errors); | ||
} | ||
} | ||
|
||
if (builder == null) { | ||
builder = new ScriptlerBuilder(builderId, null, false, Collections.emptyList()); | ||
builder = new ScriptlerBuilder(builderId, null, false, List.of()); | ||
} | ||
|
||
return builder.recreateBuilderWithBuilderIdIfRequired(); | ||
} | ||
|
||
public List<Script> getScripts() { | ||
// TODO currently only script for RUN_SCRIPT permissions are returned? | ||
Set<Script> scripts = getConfig().getScripts(); | ||
List<Script> scriptsForBuilder = new ArrayList<>(); | ||
for (Script script : scripts) { | ||
for (Script script : getConfig().getScripts()) { | ||
if (script.nonAdministerUsing) { | ||
scriptsForBuilder.add(script); | ||
} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is only meant to be temporary until jenkinsci/jenkins#10026 is merged, as per the JEP-200 blog post:
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
java.util.ImmutableCollections$ListN | ||
java.util.ImmutableCollections$List12 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not do this. Rather create a new
ArrayList
for the internal field used in the serial form.