Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Finish JENKINS-41947
  • Loading branch information
nfalco79 committed Feb 18, 2017
2 parents f0b500b + 0cd622b commit 4a60c2f
Show file tree
Hide file tree
Showing 12 changed files with 441 additions and 142 deletions.
4 changes: 3 additions & 1 deletion src/main/java/jenkins/plugins/nodejs/NodeJSBuildWrapper.java
Expand Up @@ -111,8 +111,10 @@ public void setUp(final Context context, Run<?, ?> build, FilePath workspace, La
ni = ni.forEnvironment(initialEnvironment);
ni.buildEnvVars(new EnvVarsAdapter(context));

EnvVars env = initialEnvironment.overrideAll(context.getEnv());

// add npmrc config
FilePath configFile = NodeJSUtils.supplyConfig(configId, build, workspace, listener, initialEnvironment);
FilePath configFile = NodeJSUtils.supplyConfig(configId, build, workspace, listener, env);
if (configFile != null) {
context.env(NodeJSConstants.NPM_USERCONFIG, configFile.getRemote());
}
Expand Down
23 changes: 17 additions & 6 deletions src/main/java/jenkins/plugins/nodejs/NodeJSCommandInterpreter.java
Expand Up @@ -2,7 +2,6 @@

import java.io.IOException;
import java.util.Collection;

import javax.annotation.CheckForNull;

import org.jenkinsci.Symbol;
Expand All @@ -19,8 +18,9 @@
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Environment;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.tasks.CommandInterpreter;
Expand Down Expand Up @@ -73,12 +73,13 @@ public NodeJSInstallation getNodeJS() {

/*
* (non-Javadoc)
* @see hudson.tasks.CommandInterpreter#perform(hudson.model.AbstractBuild, hudson.Launcher, hudson.model.BuildListener)
* @see hudson.tasks.CommandInterpreter#perform(hudson.model.AbstractBuild, hudson.Launcher, hudson.model.TaskListener)
*/
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException {
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, TaskListener listener) throws InterruptedException {
try {
EnvVars env = build.getEnvironment(listener);
EnvVars newEnv = new EnvVars();

// get specific installation for the node
NodeJSInstallation ni = getNodeJS();
Expand All @@ -96,7 +97,10 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen

ni = ni.forNode(node, listener);
ni = ni.forEnvironment(env);
ni.buildEnvVars(env);
ni.buildEnvVars(newEnv);

// enhance env with installation environment because is passed to supplyConfig
env.overrideAll(newEnv);

nodeExec = ni.getExecutable(launcher);
if (nodeExec == null) {
Expand All @@ -107,9 +111,12 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
// add npmrc config
FilePath configFile = NodeJSUtils.supplyConfig(configId, build, build.getWorkspace(), listener, env);
if (configFile != null) {
env.put(NodeJSConstants.NPM_USERCONFIG, configFile.getRemote());
newEnv.put(NodeJSConstants.NPM_USERCONFIG, configFile.getRemote());
}

// add an Environment so when the in super class is called build.getEnviroment() gets the enhanced env
build.getEnvironments().add(Environment.create(newEnv));

} catch (AbortException e) {
listener.fatalError(e.getMessage()); // NOSONAR
return false;
Expand All @@ -118,6 +125,10 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
e.printStackTrace(listener.fatalError(hudson.tasks.Messages.CommandInterpreter_CommandFailed()));
}

return internalPerform(build, launcher, listener);
}

protected boolean internalPerform(AbstractBuild<?, ?> build, Launcher launcher, TaskListener listener) throws InterruptedException {
return super.perform(build, launcher, listener);
}

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/jenkins/plugins/nodejs/NodeJSConstants.java
Expand Up @@ -11,6 +11,17 @@ private NodeJSConstants() {
*/
public static final String JAVASCRIPT_EXT = ".js";

/**
* The name of environment variable that point to the NodeJS installation
* home.
*/
public static final String ENVVAR_NODEJS_HOME = "NODEJS_HOME";

/**
* The name of environment variable that contribute the PATH value.
*/
public static final String ENVVAR_NODEJS_PATH = "PATH+NODEJS";

/**
* The location of user-level configuration settings.
*/
Expand Down
Expand Up @@ -49,6 +49,7 @@
import hudson.tools.ToolInstaller;
import hudson.tools.ToolProperty;
import jenkins.plugins.nodejs.Messages;
import jenkins.plugins.nodejs.NodeJSConstants;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;

Expand Down Expand Up @@ -103,8 +104,8 @@ public void buildEnvVars(EnvVars env) {
if (home == null) {
return;
}
env.put("NODEJS_HOME", home);
env.override("PATH+NODEJS", getBin());
env.put(NodeJSConstants.ENVVAR_NODEJS_HOME, home);
env.put(NodeJSConstants.ENVVAR_NODEJS_PATH, getBin());
}

/**
Expand Down
58 changes: 58 additions & 0 deletions src/test/java/jenkins/plugins/nodejs/CIBuilderHelper.java
@@ -0,0 +1,58 @@
package jenkins.plugins.nodejs;

import static org.mockito.Mockito.*;

import org.powermock.api.mockito.PowerMockito;

import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.TaskListener;
import jenkins.plugins.nodejs.tools.NodeJSInstallation;

/* package */ final class CIBuilderHelper {

public static interface Verifier {
void verify(AbstractBuild<?, ?> build, Launcher launcher, TaskListener listener) throws Exception;
}

public static NodeJSCommandInterpreter createMock(String command, NodeJSInstallation installation, String configId) {
return createMock(command, installation, configId, null);
}

public static NodeJSCommandInterpreter createMock(String command, NodeJSInstallation installation, String configId,
Verifier verifier) {
MockCommandInterpreterBuilder spy = PowerMockito.spy(new MockCommandInterpreterBuilder(command, installation.getName(), configId));
doReturn(installation).when(spy).getNodeJS();
doReturn(new NodeJSCommandInterpreter.NodeJsDescriptor()).when(spy).getDescriptor();
spy.setVerifier(verifier);
return spy;
}

static class MockCommandInterpreterBuilder extends NodeJSCommandInterpreter {

// transient to ensure serialisation
private transient CIBuilderHelper.Verifier verifier;

private MockCommandInterpreterBuilder(String command, String nodeJSInstallationName, String configId) {
super(command, nodeJSInstallationName, configId);
}

@Override
protected boolean internalPerform(AbstractBuild<?, ?> build, Launcher launcher, TaskListener listener)
throws InterruptedException {
super.internalPerform(build, launcher, listener);
if (verifier != null) {
try {
verifier.verify(build, launcher, listener);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return true;
}

private void setVerifier(Verifier verifier) {
this.verifier = verifier;
}
}
}
106 changes: 43 additions & 63 deletions src/test/java/jenkins/plugins/nodejs/NodeJSBuildWrapperTest.java
@@ -1,8 +1,8 @@
package jenkins.plugins.nodejs;

import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;
Expand All @@ -14,51 +14,54 @@
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.powermock.api.mockito.PowerMockito;

import hudson.EnvVars;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject;
import hudson.model.Node;
import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Builder;
import jenkins.plugins.nodejs.VerifyEnvVariableBuilder.FileVerifier;
import jenkins.plugins.nodejs.configfiles.NPMConfig;
import jenkins.plugins.nodejs.configfiles.NPMRegistry;
import jenkins.plugins.nodejs.configfiles.NPMConfig.NPMConfigProvider;
import jenkins.plugins.nodejs.configfiles.NPMRegistry;
import jenkins.plugins.nodejs.tools.NodeJSInstallation;

public class NodeJSBuildWrapperTest {

@Rule
public JenkinsRule j = new JenkinsRule();

@Test
public void test_calls_sequence_of_installer() throws Exception {
FreeStyleProject job = j.createFreeStyleProject("free");

NodeJSInstallation installation = mockInstaller();
NodeJSBuildWrapper bw = mockWrapper(installation, mock(NPMConfig.class));

job.getBuildWrappersList().add(bw);

j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0));

verify(installation).forNode(any(Node.class), any(TaskListener.class));
verify(installation).forEnvironment(any(EnvVars.class));
verify(installation).buildEnvVars(any(EnvVars.class));
}

@Test
public void test_creation_of_config() throws Exception {
FreeStyleProject job = j.createFreeStyleProject("free");

final Config config = createSetting("my-config-id", "email=foo@acme.com", null);

NodeJSInstallation installation = mock(NodeJSInstallation.class);
when(installation.forNode(any(Node.class), any(TaskListener.class))).then(RETURNS_SELF);
when(installation.forEnvironment(any(EnvVars.class))).then(RETURNS_SELF);
when(installation.getName()).thenReturn("mockNode");
when(installation.getHome()).thenReturn("/nodejs/home");

NodeJSBuildWrapper bw = new MockNodeJSBuildWrapper(installation, config.id);
NodeJSInstallation installation = mockInstaller();
NodeJSBuildWrapper bw = mockWrapper(installation, config);

job.getBuildWrappersList().add(bw);

job.getBuildersList().add(new FileVerifier());

j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0));

verify(installation).forNode(any(Node.class), any(TaskListener.class));
verify(installation).forEnvironment(any(EnvVars.class));
verify(installation).buildEnvVars(any(EnvVars.class));
}

@Test
Expand All @@ -67,18 +70,17 @@ public void test_inject_path_variable() throws Exception {

final Config config = createSetting("my-config-id", null, null);

String nodejsHome = new File("/home", "nodejs").getAbsolutePath(); // platform independent
final NodeJSInstallation installation = new NodeJSInstallation("inject_var", nodejsHome, null);

NodeJSBuildWrapper bw = new MockNodeJSBuildWrapper(installation, config.id);
NodeJSInstallation installation = new NodeJSInstallation("test", getTestHome(), null);
NodeJSBuildWrapper spy = mockWrapper(installation, config);

job.getBuildWrappersList().add(bw);
job.getBuildWrappersList().add(spy);

job.getBuildersList().add(new PathVerifier(installation));

j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0));
}


private Config createSetting(String id, String content, List<NPMRegistry> registries) {
String providerId = new NPMConfigProvider().getProviderId();
Config config = new NPMConfig(id, null, null, content, providerId, registries);
Expand All @@ -89,48 +91,24 @@ private Config createSetting(String id, String content, List<NPMRegistry> regist
return config;
}

private static final class MockNodeJSBuildWrapper extends NodeJSBuildWrapper {
private NodeJSInstallation installation;

public MockNodeJSBuildWrapper(NodeJSInstallation installation, String configId) {
super(installation.getName(), configId);
this.installation = installation;
}

@Override
public NodeJSInstallation getNodeJS() {
return installation;
};

@Override
public Descriptor<BuildWrapper> getDescriptor() {
return new NodeJSBuildWrapper.DescriptorImpl();
}
private NodeJSBuildWrapper mockWrapper(NodeJSInstallation installation, Config config) {
NodeJSBuildWrapper wrapper = PowerMockito.spy(new NodeJSBuildWrapper("mock", config.id));
doReturn(installation).when(wrapper).getNodeJS();
doReturn(new NodeJSBuildWrapper.DescriptorImpl()).when(wrapper).getDescriptor();
return wrapper;
}

private static abstract class VerifyEnvVariableBuilder extends Builder {
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {

EnvVars env = build.getEnvironment(listener);
verify(env);
return true;
}

public abstract void verify(EnvVars env);
private NodeJSInstallation mockInstaller() throws IOException, InterruptedException {
NodeJSInstallation mock = mock(NodeJSInstallation.class);
when(mock.forNode(any(Node.class), any(TaskListener.class))).then(RETURNS_SELF);
when(mock.forEnvironment(any(EnvVars.class))).then(RETURNS_SELF);
when(mock.getName()).thenReturn("mockNode");
when(mock.getHome()).thenReturn(getTestHome());
return mock;
}

private static final class FileVerifier extends VerifyEnvVariableBuilder {
@Override
public void verify(EnvVars env) {
String var = NodeJSConstants.NPM_USERCONFIG;
String value = env.get(var);

assertTrue("variable " + var + " not set", env.containsKey(var));
assertNotNull("empty value for environment variable " + var, value);
assertTrue("file of variable " + var + " does not exists or is not a file", new File(value).isFile());
}
private String getTestHome() {
return new File("/home", "nodejs").getAbsolutePath();
}

private static final class PathVerifier extends VerifyEnvVariableBuilder {
Expand All @@ -143,8 +121,10 @@ private PathVerifier(NodeJSInstallation installation) {
@Override
public void verify(EnvVars env) {
String expectedValue = installation.getHome();
assertEquals(env.get("NODEJS_HOME"), expectedValue);
assertEquals("Unexpected value for " + NodeJSConstants.ENVVAR_NODEJS_HOME, expectedValue, env.get(NodeJSConstants.ENVVAR_NODEJS_HOME));
assertThat(env.get("PATH"), CoreMatchers.containsString(expectedValue));
// check that PATH is not exact the NodeJS home otherwise means PATH was overridden
assertThat(env.get("PATH"), CoreMatchers.is(CoreMatchers.not(expectedValue))); // JENKINS-41947
}
}

Expand Down

0 comments on commit 4a60c2f

Please sign in to comment.