Skip to content

Commit

Permalink
Allow configuring credentials for custom Gradle plugin repository (#361)
Browse files Browse the repository at this point in the history
* Add credentials for Gradle plugin repository
* Upgrade chrome driver
* Add a few tests
  • Loading branch information
alextu authored Oct 24, 2023
1 parent d18706e commit 47bb9c5
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class BuildScansInjectionSettings extends JenkinsConfig {
private static final String GE_PLUGIN_VERSION_FIELD = "gradlePluginVersion";
private static final String GIT_REPOSITORY_FILTERS_FIELD = "vcsRepositoryFilter";
private static final String GE_ACCESS_KEY_FIELD = "accessKey";
private static final String GE_GRADLE_PLUGIN_REPOSITORY_PASSWORD_FIELD = "gradlePluginRepositoryPassword";

public BuildScansInjectionSettings(Jenkins jenkins) {
super(jenkins);
Expand Down Expand Up @@ -55,6 +56,10 @@ public void setGradleEnterpriseAccessKey(String accessKey) {
setBuildScansInjectionFormValue(GE_ACCESS_KEY_FIELD, accessKey);
}

public void setGradleEnterpriseGradlePluginRepoPassword(String password) {
setBuildScansInjectionFormValue(GE_GRADLE_PLUGIN_REPOSITORY_PASSWORD_FIELD, password);
}

public void setGradleEnterprisePluginVersion(String version) {
setBuildScansInjectionFormValue(GE_PLUGIN_VERSION_FIELD, version);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ protected final void enableBuildScansForMaven() {
});
}

protected final void setGradlePluginRepositoryPassword(String password) {
updateBuildScansInjectionSettings(settings -> {
settings.setGradleEnterpriseGradlePluginRepoPassword(password);
});
}

private void updateBuildScansInjectionSettings(Consumer<BuildScansInjectionSettings> spec) {
BuildScansInjectionSettings settings = new BuildScansInjectionSettings(jenkins);
settings.configure();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,29 @@ public void accessKeyIsMasked() {
build.action(EnvInjectAction.class).shouldContain("GRADLE_ENTERPRISE_ACCESS_KEY", "[*******]");
}

@Test
@WithPlugins("envinject")
public void gradlePluginRepoPasswordIsMasked() {
// given
setGradlePluginRepositoryPassword("foo");

FreeStyleJob job = jenkins.jobs.create(FreeStyleJob.class);
job.copyDir(resource("/simple_gradle_project"));
GradleStep gradle = job.addBuildStep(GradleStep.class);
gradle.setVersion(GRADLE_VERSION);
gradle.setSwitches("--no-daemon");
gradle.setTasks("helloWorld");
job.save();

// when
Build build = job.startBuild();

// then
build.shouldSucceed();
assertBuildScanPublished(build);
build.action(EnvInjectAction.class).shouldContain("JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD", "[*******]");
}

@Test
public void logsErrorIfBuildScanUploadFailed() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import org.gradle.api.internal.artifacts.transform.UnzipTransform
import org.gradle.internal.os.OperatingSystem

// Latest version: https://chromedriver.storage.googleapis.com/LATEST_RELEASE
val chromeDriverVersion = "116.0.5845.96"
val chromeDriverVersion = "117.0.5938.88"
val ciTeamCityBuild: Boolean by (gradle as ExtensionAware).extra

val os: OperatingSystem = OperatingSystem.current()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,28 @@
import hudson.util.Secret;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static hudson.plugins.gradle.injection.GradleInjectionAware.JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD;

@Extension
public class BuildScanEnvironmentContributor extends EnvironmentContributor {

@Override
public void buildEnvironmentFor(@Nonnull Run run, @Nonnull EnvVars envs, @Nonnull TaskListener listener) {
Secret secret = InjectionConfig.get().getAccessKey();
if (secret == null || alreadyExecuted(run)) {
return;
}

String accessKey = secret.getPlainText();
if (!GradleEnterpriseAccessKeyValidator.getInstance().isValid(accessKey)) {
GradleEnterpriseLogger logger = new GradleEnterpriseLogger(listener);
logger.error("Gradle Enterprise access key format is not valid");
run.addAction(GradleEnterpriseParametersAction.empty());
Secret secretKey = InjectionConfig.get().getAccessKey();
Secret secretPassword = InjectionConfig.get().getGradlePluginRepositoryPassword();
if ((secretKey == null && secretPassword == null) || alreadyExecuted(run)) {
return;
}

run.addAction(GradleEnterpriseParametersAction.of(accessKey));
run.addAction(GradleEnterpriseParametersAction.of(new GradleEnterpriseLogger(listener), secretKey, secretPassword));
}

private static boolean alreadyExecuted(@Nonnull Run run) {
Expand All @@ -45,7 +43,7 @@ private static boolean alreadyExecuted(@Nonnull Run run) {
public static class GradleEnterpriseParametersAction extends ParametersAction {

private static final String GRADLE_ENTERPRISE_ACCESS_KEY = "GRADLE_ENTERPRISE_ACCESS_KEY";
private static final Set<String> ADDITIONAL_SAFE_PARAMETERS = Collections.singleton(GRADLE_ENTERPRISE_ACCESS_KEY);
private static final String GRADLE_ENTERPRISE_REPO_PASSWORD = JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD;

private static final GradleEnterpriseParametersAction EMPTY = new GradleEnterpriseParametersAction();

Expand All @@ -61,10 +59,24 @@ static GradleEnterpriseParametersAction empty() {
return EMPTY;
}

static GradleEnterpriseParametersAction of(String accessKey) {
static GradleEnterpriseParametersAction of(GradleEnterpriseLogger logger, @Nullable Secret accessKey, @Nullable Secret repoPassword) {
List<ParameterValue> values = new ArrayList<>();
if (accessKey != null) {
if (!GradleEnterpriseAccessKeyValidator.getInstance().isValid(accessKey.getPlainText())) {
logger.error("Gradle Enterprise access key format is not valid");
} else {
values.add(new PasswordParameterValue(GRADLE_ENTERPRISE_ACCESS_KEY, accessKey.getPlainText()));
}
}
if (repoPassword != null) {
values.add(new PasswordParameterValue(GRADLE_ENTERPRISE_REPO_PASSWORD, repoPassword.getPlainText()));
}
if (values.isEmpty()) {
return GradleEnterpriseParametersAction.empty();
}
return new GradleEnterpriseParametersAction(
Collections.singletonList(new PasswordParameterValue(GRADLE_ENTERPRISE_ACCESS_KEY, accessKey, null)),
ADDITIONAL_SAFE_PARAMETERS
values,
Stream.of(GRADLE_ENTERPRISE_ACCESS_KEY, GRADLE_ENTERPRISE_REPO_PASSWORD).collect(Collectors.toSet())
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ private void injectEnvironmentVariables(InjectionConfig config, Node node) {
if (pluginRepositoryUrl != null && InjectionUtil.isValid(InjectionConfig.checkUrl(pluginRepositoryUrl))) {
EnvUtil.setEnvVar(node, JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_URL, pluginRepositoryUrl);
}

if (config.getGradlePluginRepositoryUsername() != null) {
EnvUtil.setEnvVar(node, JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_USERNAME, config.getGradlePluginRepositoryUsername());
} else {
EnvUtil.removeEnvVar(node, JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_USERNAME);
}

String ccudPluginVersion = config.getCcudPluginVersion();
if (ccudPluginVersion != null && InjectionUtil.isValid(InjectionConfig.checkVersion(ccudPluginVersion))) {
EnvUtil.setEnvVar(node, JENKINSGRADLEPLUGIN_CCUD_PLUGIN_VERSION, ccudPluginVersion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public interface GradleInjectionAware {
String JENKINSGRADLEPLUGIN_GRADLE_ENTERPRISE_ENFORCE_URL = "JENKINSGRADLEPLUGIN_GRADLE_ENTERPRISE_ENFORCE_URL";
String JENKINSGRADLEPLUGIN_GRADLE_ENTERPRISE_ALLOW_UNTRUSTED_SERVER = "JENKINSGRADLEPLUGIN_GRADLE_ENTERPRISE_ALLOW_UNTRUSTED_SERVER";
String JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_URL = "JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_URL";
String JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_USERNAME = "JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_USERNAME";
String JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD = "JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD";
String JENKINSGRADLEPLUGIN_GRADLE_ENTERPRISE_PLUGIN_VERSION = "JENKINSGRADLEPLUGIN_GRADLE_ENTERPRISE_PLUGIN_VERSION";
String JENKINSGRADLEPLUGIN_CCUD_PLUGIN_VERSION = "JENKINSGRADLEPLUGIN_CCUD_PLUGIN_VERSION";

Expand Down
26 changes: 26 additions & 0 deletions src/main/java/hudson/plugins/gradle/injection/InjectionConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class InjectionConfig extends GlobalConfiguration {
private String gradlePluginVersion;
private String ccudPluginVersion;
private String gradlePluginRepositoryUrl;
private String gradlePluginRepositoryUsername;
private Secret gradlePluginRepositoryPassword;
private ImmutableList<NodeLabelItem> gradleInjectionEnabledNodes;
private ImmutableList<NodeLabelItem> gradleInjectionDisabledNodes;

Expand Down Expand Up @@ -148,6 +150,30 @@ public void setAccessKey(Secret accessKey) {
}
}

@CheckForNull
public String getGradlePluginRepositoryUsername() {
return gradlePluginRepositoryUsername;
}

@DataBoundSetter
public void setGradlePluginRepositoryUsername(String gradlePluginRepositoryUsername) {
this.gradlePluginRepositoryUsername = Util.fixEmptyAndTrim(gradlePluginRepositoryUsername);
}

@CheckForNull
public Secret getGradlePluginRepositoryPassword() {
return gradlePluginRepositoryPassword;
}

@DataBoundSetter
public void setGradlePluginRepositoryPassword(Secret gradlePluginRepositoryPassword) {
if (Util.fixEmptyAndTrim(gradlePluginRepositoryPassword.getPlainText()) == null) {
this.gradlePluginRepositoryPassword = null;
} else {
this.gradlePluginRepositoryPassword = gradlePluginRepositoryPassword;
}
}

@CheckForNull
public String getGradlePluginVersion() {
return gradlePluginVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
<f:entry title="${%Gradle plugin repository url}" field="gradlePluginRepositoryUrl">
<f:textbox checkMethod="post"/>
</f:entry>
<f:entry title="${%Gradle plugin repository username}" field="gradlePluginRepositoryUsername">
<f:textbox checkMethod="post"/>
</f:entry>
<f:entry title="${%Gradle plugin repository password}" field="gradlePluginRepositoryPassword">
<f:password/>
</f:entry>
<f:entry title="${%Gradle auto-injection enabled nodes}"
help="/plugin/gradle/help-gradleInjectionEnabledNodes.html">
<f:repeatableProperty field="gradleInjectionEnabledNodes">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,28 @@ initscript {
def pluginRepositoryUrl = getInputParam('jenkinsGradlePlugin.gradle.plugin-repository.url')
def gePluginVersion = getInputParam('jenkinsGradlePlugin.gradle-enterprise.plugin.version')
def ccudPluginVersion = getInputParam('jenkinsGradlePlugin.ccud.plugin.version')
def gradlePluginRepoUsername = getInputParam('jenkinsGradlePlugin.gradle.plugin-repository.username')
def gradlePluginRepoPassword = getInputParam('jenkinsGradlePlugin.gradle.plugin-repository.password')

def atLeastGradle5 = GradleVersion.current() >= GradleVersion.version('5.0')
def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0')

if (gePluginVersion || ccudPluginVersion && atLeastGradle4) {
pluginRepositoryUrl = pluginRepositoryUrl ?: 'https://plugins.gradle.org/m2'
logger.quiet("Gradle Enterprise plugins resolution: $pluginRepositoryUrl")

repositories {
maven { url pluginRepositoryUrl }
maven {
url pluginRepositoryUrl
if (gradlePluginRepoUsername && gradlePluginRepoPassword) {
credentials {
username(gradlePluginRepoUsername)
password(gradlePluginRepoPassword)
}
authentication {
basic(BasicAuthentication)
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package hudson.plugins.gradle.injection

import hudson.EnvVars
import hudson.model.PasswordParameterValue
import hudson.model.ParameterValue
import hudson.model.Run
import hudson.model.TaskListener
import hudson.plugins.gradle.BaseJenkinsIntegrationTest
Expand Down Expand Up @@ -29,6 +29,19 @@ class BuildScanEnvironmentContributorTest extends BaseJenkinsIntegrationTest {
0 * run.addAction(_)
}

def 'does nothing if no password'() {
given:
def config = InjectionConfig.get()
config.setGradlePluginRepositoryPassword(Secret.fromString(""))
config.save()

when:
buildScanEnvironmentContributor.buildEnvironmentFor(run, new EnvVars(), TaskListener.NULL)

then:
0 * run.addAction(_)
}

def 'adds empty action if access key is invalid'() {
given:
def config = InjectionConfig.get()
Expand All @@ -44,6 +57,24 @@ class BuildScanEnvironmentContributorTest extends BaseJenkinsIntegrationTest {
}
}

def 'adds action if access key is invalid but password is there'() {
given:
def config = InjectionConfig.get()
config.setAccessKey(Secret.fromString("secret"))
config.setGradlePluginRepositoryPassword(Secret.fromString("foo"))
config.save()

when:
buildScanEnvironmentContributor.buildEnvironmentFor(run, new EnvVars(), TaskListener.NULL)

then:
1 * run.addAction { GradleEnterpriseParametersAction action ->
def parameters = action.getAllParameters()
parameters.size() == 1
paramEquals(parameters.first(), 'JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD', 'foo')
}
}

def 'adds an action with the access key'() {
given:
def accessKey = "server=secret"
Expand All @@ -58,7 +89,48 @@ class BuildScanEnvironmentContributorTest extends BaseJenkinsIntegrationTest {
1 * run.addAction { GradleEnterpriseParametersAction action ->
def parameters = action.getAllParameters()
parameters.size() == 1
parameters.first() == new PasswordParameterValue('GRADLE_ENTERPRISE_ACCESS_KEY', accessKey, null)
paramEquals(parameters.first(), 'GRADLE_ENTERPRISE_ACCESS_KEY', accessKey)
}
}

def 'adds an action with the password'() {
given:
def config = InjectionConfig.get()
config.setGradlePluginRepositoryPassword(Secret.fromString("foo"))
config.save()

when:
buildScanEnvironmentContributor.buildEnvironmentFor(run, new EnvVars(), TaskListener.NULL)

then:
1 * run.addAction { GradleEnterpriseParametersAction action ->
def parameters = action.getAllParameters()
parameters.size() == 1
paramEquals(parameters.first(), 'JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD', 'foo')
}
}

def 'adds an action with access key and password'() {
given:
def config = InjectionConfig.get()
config.setAccessKey(Secret.fromString("server=secret"))
config.setGradlePluginRepositoryPassword(Secret.fromString("foo"))
config.save()

when:
buildScanEnvironmentContributor.buildEnvironmentFor(run, new EnvVars(), TaskListener.NULL)

then:
1 * run.addAction { GradleEnterpriseParametersAction action ->
def parameters = action.getAllParameters()
parameters.size() == 2
paramEquals(parameters[0], 'GRADLE_ENTERPRISE_ACCESS_KEY', 'server=secret')
paramEquals(parameters[1], 'JENKINSGRADLEPLUGIN_GRADLE_PLUGIN_REPOSITORY_PASSWORD', 'foo')
}
}

private boolean paramEquals(ParameterValue param, String name, String value) {
param.name == name && param.value?.plainText == value
}

}

0 comments on commit 47bb9c5

Please sign in to comment.