Skip to content

Commit

Permalink
[FIX JENKINS-29266] Add HTTP_PROXY and HTTPS_PROXY variables to the p…
Browse files Browse the repository at this point in the history
…rocess environment that execute the npm install of global packages.

The proxy URL is the same used to download the nodejs installer and could be setup in Jenkins configuration.
  • Loading branch information
nfalco79 committed Feb 18, 2017
1 parent 5050b8d commit 625be45
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 6 deletions.
5 changes: 5 additions & 0 deletions src/main/java/jenkins/plugins/nodejs/NodeJSConstants.java
Expand Up @@ -11,6 +11,11 @@ private NodeJSConstants() {
*/
public static final String JAVASCRIPT_EXT = ".js";

/**
* Default NPM registry.
*/
public static final String DEFAULT_NPM_REGISTRY = "registry.npmjs.org";

/**
* The name of environment variable that point to the NodeJS installation
* home.
Expand Down
36 changes: 35 additions & 1 deletion src/main/java/jenkins/plugins/nodejs/tools/NodeJSInstaller.java
Expand Up @@ -25,6 +25,9 @@

import java.io.File;
import java.io.IOException;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
Expand All @@ -43,6 +46,7 @@
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Functions;
Expand All @@ -56,8 +60,10 @@
import hudson.tools.DownloadFromUrlInstaller;
import hudson.tools.ToolInstallation;
import hudson.util.ArgumentListBuilder;
import hudson.util.Secret;
import jenkins.MasterToSlaveFileCallable;
import jenkins.plugins.nodejs.Messages;
import jenkins.plugins.nodejs.NodeJSConstants;
import jenkins.plugins.tools.Installables;

/**
Expand Down Expand Up @@ -173,8 +179,16 @@ protected void refreshGlobalPackages(Node node, TaskListener log, FilePath expec
npmScriptArgs.add(packageName);
}

EnvVars env = new EnvVars();
env.put(NodeJSConstants.ENVVAR_NODEJS_PATH, binFolder.getRemote());
try {
buildProxyEnvVars(env);
} catch (URISyntaxException e) {
log.error("Wrong proxy URL: " + e.getMessage());
}

hudson.Launcher launcher = node.createLauncher(log);
int returnCode = launcher.launch().envs("PATH+NODEJS=" + binFolder.getRemote()).cmds(npmScriptArgs).stdout(log).join();
int returnCode = launcher.launch().envs(env).cmds(npmScriptArgs).stdout(log).join();

if (returnCode == 0) {
// leave a record for the next up-to-date check
Expand All @@ -185,6 +199,26 @@ protected void refreshGlobalPackages(Node node, TaskListener log, FilePath expec
}
}

private void buildProxyEnvVars(EnvVars env) throws IOException, URISyntaxException {
// Should be read from npmrc file ?
String registry = NodeJSConstants.DEFAULT_NPM_REGISTRY;

ProxyConfiguration proxycfg = ProxyConfiguration.load();
if (proxycfg != null && proxycfg.createProxy(registry) != Proxy.NO_PROXY) {
String userInfo = proxycfg.getUserName() != null ? proxycfg.getUserName() : null;
// append password only if userName if is defined
if (userInfo != null && proxycfg.getEncryptedPassword() != null) {
userInfo += ":" + Secret.decrypt(proxycfg.getEncryptedPassword());
}

String proxyURL = new URI("http", userInfo, proxycfg.name, proxycfg.port, null, null, null).toString();

// refer to https://docs.npmjs.com/misc/config#https-proxy
env.put("HTTP_PROXY", proxyURL);
env.put("HTTPS_PROXY", proxyURL);
}
}

public static boolean areNpmPackagesUpToDate(FilePath expected, String npmPackages, long npmPackagesRefreshHours) throws IOException, InterruptedException {
FilePath marker = expected.child(NPM_PACKAGES_RECORD_FILENAME);
return marker.exists() && marker.readToString().equals(npmPackages) && System.currentTimeMillis() < marker.lastModified()+ TimeUnit.HOURS.toMillis(npmPackagesRefreshHours);
Expand Down
@@ -0,0 +1,64 @@
package jenkins.plugins.nodejs.tools;

import static org.junit.Assert.*;

import java.net.MalformedURLException;
import java.net.URL;
import org.hamcrest.CoreMatchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.powermock.reflect.Whitebox;

import hudson.EnvVars;
import hudson.ProxyConfiguration;

@Issue("JENKINS-29266")
@RunWith(Parameterized.class)
public class NodeJSInstallerProxyTest {

@Parameters(name = "proxy url = {0}")
public static String[] data() throws MalformedURLException {
return new String[] { "http://proxy.example.org:8080", "http://user:password@proxy.example.org:8080" };
}

@Rule
public JenkinsRule r = new JenkinsRule();
private String host;
private int port;
private String username;
private String password;
private String expectedURL;

public NodeJSInstallerProxyTest(String url) throws Exception {
URL proxyURL = new URL(url);

this.expectedURL = url;
this.host = proxyURL.getHost();
this.port = proxyURL.getPort();
if (proxyURL.getUserInfo() != null) {
String[] userInfo = proxyURL.getUserInfo().split(":");
this.username = userInfo[0];
this.password = userInfo[1];
}
}

@Test
public void test_proxy_settings() throws Exception {
ProxyConfiguration proxycfg = new ProxyConfiguration(host, port, username, password);
proxycfg.save();

NodeJSInstaller installer = new NodeJSInstaller("test-id", "grunt", NodeJSInstaller.DEFAULT_NPM_PACKAGES_REFRESH_HOURS);
EnvVars env = new EnvVars();
Whitebox.invokeMethod(installer, "buildProxyEnvVars", env);

assertThat(env.keySet(), CoreMatchers.hasItems("HTTP_PROXY", "HTTPS_PROXY"));
assertThat(env.get("HTTP_PROXY"), CoreMatchers.is(expectedURL));
assertThat(env.get("HTTPS_PROXY"), CoreMatchers.is(expectedURL));
}

}
@@ -1,4 +1,4 @@
package jenkins.plugins.nodejs;
package jenkins.plugins.nodejs.tools;

import static org.mockito.Mockito.*;

Expand All @@ -13,9 +13,6 @@
import hudson.model.TaskListener;
import hudson.tools.DownloadFromUrlInstaller;
import hudson.tools.ToolInstallation;
import jenkins.plugins.nodejs.tools.CPU;
import jenkins.plugins.nodejs.tools.NodeJSInstaller;
import jenkins.plugins.nodejs.tools.Platform;

@RunWith(PowerMockRunner.class)
@PrepareForTest(NodeJSInstaller.class)
Expand All @@ -37,7 +34,7 @@ public void test_skip_install_global_packages_when_empty() throws Exception {
int expectedRefreshHours = NodeJSInstaller.DEFAULT_NPM_PACKAGES_REFRESH_HOURS;
Node currentNode = mock(Node.class);

// mock all the static methods in a class called "Static"
// mock all the static methods in the class
PowerMockito.mockStatic(NodeJSInstaller.class);

// create partial mock
Expand Down

0 comments on commit 625be45

Please sign in to comment.