Skip to content

Commit

Permalink
Merge pull request #45 from jglick/shared-libs-JENKINS-31155
Browse files Browse the repository at this point in the history
[JENKINS-31155] Infrastructure for shared libraries
  • Loading branch information
jglick committed Sep 7, 2016
2 parents 4969ce0 + b5b11b0 commit 1100650
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 101 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Expand Up @@ -28,8 +28,8 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.12</version>
<relativePath />
<version>2.14</version>
<relativePath/>
</parent>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
Expand Down Expand Up @@ -75,7 +75,7 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>workflow-api</artifactId>
<version>2.1</version>
<version>2.3</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
Expand Down
41 changes: 26 additions & 15 deletions src/main/java/org/jenkinsci/plugins/workflow/cps/CpsScript.java
Expand Up @@ -34,6 +34,8 @@
import hudson.model.Run;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
Expand All @@ -55,6 +57,8 @@
@PersistIn(PROGRAM)
public abstract class CpsScript extends SerializableScript {

private static final Logger LOGGER = Logger.getLogger(CpsScript.class.getName());

private static final String STEPS_VAR = "steps";

transient CpsFlowExecution execution;
Expand Down Expand Up @@ -101,14 +105,13 @@ public CpsScript() throws IOException {
public final Object invokeMethod(String name, Object args) {
// if global variables are defined by that name, try to call it.
// the 'call' convention comes from Closure
for (GlobalVariable v : GlobalVariable.ALL) {
if (v.getName().equals(name)) {
try {
Object o = v.getValue(this);
return InvokerHelper.getMetaClass(o).invokeMethod(o,"call",args);
} catch (Exception x) {
throw new InvokerInvocationException(x);
}
GlobalVariable v = GlobalVariable.byName(name, $buildNoException());
if (v != null) {
try {
Object o = v.getValue(this);
return InvokerHelper.getMetaClass(o).invokeMethod(o, "call", args);
} catch (Exception x) {
throw new InvokerInvocationException(x);
}
}

Expand All @@ -119,13 +122,12 @@ public final Object invokeMethod(String name, Object args) {

@Override
public Object getProperty(String property) {
for (GlobalVariable v : GlobalVariable.ALL) {
if (v.getName().equals(property)) {
try {
return v.getValue(this);
} catch (Exception x) {
throw new InvokerInvocationException(x);
}
GlobalVariable v = GlobalVariable.byName(property, $buildNoException());
if (v != null) {
try {
return v.getValue(this);
} catch (Exception x) {
throw new InvokerInvocationException(x);
}
}
return super.getProperty(property);
Expand All @@ -141,6 +143,15 @@ public Object getProperty(String property) {
}
}

public @CheckForNull Run<?,?> $buildNoException() {
try {
return $build();
} catch (IOException x) {
LOGGER.log(Level.WARNING, null, x);
return null;
}
}

@Override
public Object evaluate(String script) throws CompilationFailedException {
// this might throw the magic CpsCallableInvocation to execute the script asynchronously
Expand Down
Expand Up @@ -38,10 +38,8 @@ public boolean permitsMethod(Method method, Object receiver, Object[] args) {
return true;
}
if (name.equals("getProperty") && args.length == 1 && args[0] instanceof String) {
for (GlobalVariable v : GlobalVariable.ALL) {
if (v.getName().equals(args[0])) {
return true;
}
if (GlobalVariable.byName((String) args[0], ((CpsScript) receiver).$buildNoException()) != null) {
return true;
}
}
}
Expand Down Expand Up @@ -80,7 +78,7 @@ public boolean permitsStaticMethod(Method method, Object[] args) {
*/
private static final Map<Jenkins,Whitelist> wrappedByJenkins = new WeakHashMap<Jenkins,Whitelist>();

public static synchronized Whitelist get() {
static synchronized Whitelist get() {
Jenkins j = Jenkins.getInstance();
if (j == null) {
return new ProxyWhitelist();
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/org/jenkinsci/plugins/workflow/cps/DSL.java
Expand Up @@ -32,6 +32,8 @@
import groovy.lang.GroovyObjectSupport;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.TaskListener;
import org.jenkinsci.plugins.structs.describable.DescribableModel;
import org.jenkinsci.plugins.structs.describable.DescribableParameter;
Expand Down Expand Up @@ -128,18 +130,23 @@ public Object invokeMethod(String name, Object args) {
}

Set<String> symbols = new TreeSet<>();
Set<String> globals = new TreeSet<>();
// TODO SymbolLookup only lets us find a particular symbol, not enumerate them
try {
for (Class<?> e : Index.list(Symbol.class, Jenkins.getActiveInstance().pluginManager.uberClassLoader, Class.class)) {
if (Descriptor.class.isAssignableFrom(e)) {
symbols.addAll(SymbolLookup.getSymbolValue(e));
}
}
Queue.Executable executable = exec.getOwner().getExecutable();
for (GlobalVariable var : GlobalVariable.forRun(executable instanceof Run ? (Run) executable : null)) {
globals.add(var.getName());
}
} catch (IOException x) {
Logger.getLogger(DSL.class.getName()).log(Level.WARNING, null, x);
}
// TODO probably this should be throwing a subtype of groovy.lang.MissingMethodException
throw new NoSuchMethodError("No such DSL method '" + name + "' found among steps " + functions.keySet() + " or symbols " + symbols);
throw new NoSuchMethodError("No such DSL method '" + name + "' found among steps " + functions.keySet() + " or symbols " + symbols + " or globals " + globals);
}

/**
Expand Down
Expand Up @@ -29,6 +29,7 @@
import hudson.Extension;
import hudson.model.EnvironmentContributor;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.util.LogTaskListener;
import java.io.IOException;
import java.io.Serializable;
Expand All @@ -40,6 +41,7 @@
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import jenkins.model.RunAction2;
import org.jenkinsci.plugins.workflow.flow.FlowCopier;
import org.jenkinsci.plugins.workflow.steps.EnvironmentExpander;
import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction;
import org.kohsuke.accmod.Restricted;
Expand Down Expand Up @@ -133,10 +135,10 @@ private EnvActionImpl() {
@Override public String getName() {
return "env";
}
@Override public Object getValue(CpsScript script) throws Exception {
@Override public EnvActionImpl getValue(CpsScript script) throws Exception {
Run<?,?> run = script.$build();
if (run != null) {
return forRun(run);
return EnvActionImpl.forRun(run);
} else {
throw new IllegalStateException("no associated build");
}
Expand All @@ -147,4 +149,19 @@ public Collection<EnvironmentContributor> getEnvironmentContributors() {
}
}

@Restricted(DoNotUse.class)
@Extension public static class Copier extends FlowCopier.ByRun {

@Override public void copy(Run<?,?> original, Run<?,?> copy, TaskListener listener) throws IOException, InterruptedException {
EnvActionImpl orig = original.getAction(EnvActionImpl.class);
if (orig != null) {
EnvActionImpl nue = EnvActionImpl.forRun(copy);
for (Map.Entry<String,String> entry : orig.getOverriddenEnvironment().entrySet()) {
nue.setProperty(entry.getKey(), entry.getValue());
}
}
}

}

}
Expand Up @@ -27,11 +27,14 @@
import groovy.lang.GroovyObject;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Job;
import hudson.model.Run;
import hudson.util.Iterators.FlattenIterator;
import jenkins.model.RunAction2;

import javax.annotation.Nonnull;
import java.util.Iterator;
import javax.annotation.CheckForNull;

/**
* Defines a provider of a global variable offered to flows.
Expand Down Expand Up @@ -63,17 +66,58 @@ public abstract class GlobalVariable implements ExtensionPoint {
public abstract @Nonnull Object getValue(@Nonnull CpsScript script) throws Exception;

/**
* Returns all the registered {@link GlobalVariable}s.
* @deprecated use {@link #forRun} instead
*/
public static final Iterable<GlobalVariable> ALL = new Iterable<GlobalVariable>() {
@Override
public Iterator<GlobalVariable> iterator() {
return new FlattenIterator<GlobalVariable,GlobalVariableSet>(ExtensionList.lookup(GlobalVariableSet.class).iterator()) {
@Override
protected Iterator<GlobalVariable> expand(GlobalVariableSet vs) {
return vs.iterator();
}
};
@Deprecated
public static final Iterable<GlobalVariable> ALL = forRun(null);

/**
* Returns all the registered {@link GlobalVariable}s for some context.
* @param run see {@link GlobalVariableSet#forRun}
* @return a possibly empty list
*/
public static @Nonnull Iterable<GlobalVariable> forRun(@CheckForNull final Run<?,?> run) {
return new Iterable<GlobalVariable>() {
@Override public Iterator<GlobalVariable> iterator() {
return new FlattenIterator<GlobalVariable,GlobalVariableSet>(ExtensionList.lookup(GlobalVariableSet.class).iterator()) {
@Override protected Iterator<GlobalVariable> expand(GlobalVariableSet vs) {
return vs.forRun(run).iterator();
}
};
}
};
}

/**
* Returns all the registered {@link GlobalVariable}s for some context.
* @param job see {@link GlobalVariableSet#forJob}
* @return a possibly empty list
*/
public static @Nonnull Iterable<GlobalVariable> forJob(@CheckForNull final Job<?,?> job) {
return new Iterable<GlobalVariable>() {
@Override public Iterator<GlobalVariable> iterator() {
return new FlattenIterator<GlobalVariable,GlobalVariableSet>(ExtensionList.lookup(GlobalVariableSet.class).iterator()) {
@Override protected Iterator<GlobalVariable> expand(GlobalVariableSet vs) {
return vs.forJob(job).iterator();
}
};
}
};
}

/**
* Finds a particular variable by name.
* @param name see {@link #getName}
* @param run see {@link GlobalVariableSet#forRun}
* @return the first matching variable, or null if there is none
*/
public static @CheckForNull GlobalVariable byName(@Nonnull String name, @CheckForNull Run<?,?> run) {
for (GlobalVariable var : forRun(run)) {
if (var.getName().equals(name)) {
return var;
}
}
};
return null;
}

}
@@ -1,13 +1,19 @@
package org.jenkinsci.plugins.workflow.cps;

import com.google.common.collect.Lists;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.Util;
import hudson.model.Job;
import hudson.model.Run;
import java.util.Collection;
import java.util.Iterator;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import java.util.Iterator;

/**
* Extension point that defines a collection of global variables.
*
Expand All @@ -16,6 +22,34 @@
*/
public abstract class GlobalVariableSet implements ExtensionPoint, Iterable<GlobalVariable> {

/**
* Enumerate all global variables from this provider which should be associated with a given build.
* @param run a build, which may or may not still be running; or may be left null to look for variables that exist without any context
* @return a possibly empty set
*/
public /* abstract */ @Nonnull Collection<GlobalVariable> forRun(@CheckForNull Run<?,?> run) {
return Lists.newArrayList(iterator());
}

/**
* Enumerate all global variables from this provider which should be associated with a given job.
* @param job a job; or may be left null to look for variables that exist without any context
* @return a possibly empty set; by default delegates to {@link #forRun} on {@link Job#getLastSuccessfulBuild}
*/
public @Nonnull Collection<GlobalVariable> forJob(@CheckForNull Job<?,?> job) {
return forRun(job != null ? job.getLastSuccessfulBuild() : null);
}

/** @deprecated implement {@link #forRun} instead */
@Deprecated
@Override public Iterator<GlobalVariable> iterator() {
if (Util.isOverridden(GlobalVariableSet.class, getClass(), "forRun", Run.class)) {
return forRun(null).iterator();
} else {
throw new AbstractMethodError(getClass().getName() + " must implement forRun");
}
}

/**
* Allow {@link GlobalVariable}s to be defined with {@link Extension}, and make them discoverable
* via {@link GlobalVariableSet}. This simplifies the registration of single global variable.
Expand All @@ -24,8 +58,8 @@ public abstract class GlobalVariableSet implements ExtensionPoint, Iterable<Glob
@Restricted(NoExternalUse.class)
public static class GlobalVariableProvider extends GlobalVariableSet {
@Override
public Iterator<GlobalVariable> iterator() {
return ExtensionList.lookup(GlobalVariable.class).iterator();
public Collection<GlobalVariable> forRun(Run<?,?> run) {
return ExtensionList.lookup(GlobalVariable.class);
}
}
}
Expand Up @@ -64,6 +64,7 @@
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;

/**
Expand Down Expand Up @@ -451,7 +452,8 @@ public String getSymbol() {
@Restricted(DoNotUse.class) // for stapler
public Iterable<GlobalVariable> getGlobalVariables() {
// TODO order TBD. Alphabetical? Extension.ordinal?
return GlobalVariable.ALL;
StaplerRequest req = Stapler.getCurrentRequest();
return GlobalVariable.forJob(req != null ? req.findAncestorObject(Job.class) : null);
}

@Restricted(NoExternalUse.class)
Expand Down

0 comments on commit 1100650

Please sign in to comment.