From 8255bde2a361de32ffc72f69568e7cecd1350348 Mon Sep 17 00:00:00 2001
From: Ronny Perinke <23166289+sephiroth-j@users.noreply.github.com>
Date: Thu, 28 Nov 2024 21:01:58 +0100
Subject: [PATCH] Support variables in project properties
fixes JENKINS-74822
---
CHANGELOG.md | 2 ++
README.md | 2 ++
.../DependencyTrackPublisher.java | 25 ++++++++++++++++---
.../ProjectProperties/help-description.html | 1 +
.../help-description_de.html | 1 +
.../ProjectProperties/help-group.html | 1 +
.../ProjectProperties/help-group_de.html | 1 +
.../ProjectProperties/help-parentId.html | 1 +
.../ProjectProperties/help-parentId_de.html | 1 +
.../ProjectProperties/help-parentName.html | 1 +
.../ProjectProperties/help-parentName_de.html | 1 +
.../ProjectProperties/help-parentVersion.html | 1 +
.../help-parentVersion_de.html | 1 +
.../ProjectProperties/help-swidTagId.html | 1 +
.../ProjectProperties/help-swidTagId_de.html | 1 +
.../ProjectProperties/help-tags.html | 1 +
.../ProjectProperties/help-tags_de.html | 1 +
.../DependencyTrackPublisherTest.java | 23 ++++++++++++++---
18 files changed, 58 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97d9c28b..7a4bf880 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
## Unreleased
### ⚠ Breaking
### ⭐ New Features
+- Support variables in project properties ([JENKINS-74822](https://issues.jenkins.io/browse/JENKINS-74822))
+
### 🐞 Bugs Fixed
## v5.1.0 - 2024-09-20
diff --git a/README.md b/README.md
index f6f411c8..f0daf925 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,8 @@ Once configured with a valid URL and API key, simply configure a job to publish
- description
- ID of parent project (for Dependency-Track v4.7 and newer)
+ The use of environment variables in the form `${VARIABLE}` is supported here.
+
**Override global settings**: Allows to override global settings for "Auto Create Projects", "Dependency-Track URL", "Dependency-Track Frontend URL", "API key", "Polling Interval" and the various timeouts.
### Thresholds
diff --git a/src/main/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher.java b/src/main/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher.java
index c23bd14d..043edac2 100644
--- a/src/main/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher.java
+++ b/src/main/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher.java
@@ -32,6 +32,7 @@
import hudson.tasks.Recorder;
import hudson.util.Secret;
import java.util.Optional;
+import java.util.stream.Collectors;
import jenkins.model.RunAction2;
import jenkins.tasks.SimpleBuildStep;
import lombok.AccessLevel;
@@ -320,10 +321,11 @@ public void perform(@NonNull final Run, ?> run, @NonNull final FilePath worksp
final String effectiveUrl = getEffectiveUrl();
final String effectiveApiKey = getEffectiveApiKey(run);
+ final ProjectProperties effectiveProjectProperties = expandProjectProperties(env);
logger.log(Messages.Builder_Publishing(effectiveUrl, effectiveArtifact));
final ApiClient apiClient = clientFactory.create(effectiveUrl, effectiveApiKey, logger, getEffectiveConnectionTimeout(), getEffectiveReadTimeout());
final UploadResult uploadResult = apiClient.upload(projectId, effectiveProjectName, effectiveProjectVersion,
- artifactFilePath, effectiveAutocreate, projectProperties);
+ artifactFilePath, effectiveAutocreate, effectiveProjectProperties);
if (!uploadResult.isSuccess()) {
throw new AbortException(Messages.Builder_Upload_Failed());
@@ -337,7 +339,7 @@ public void perform(@NonNull final Run, ?> run, @NonNull final FilePath worksp
logger.log(Messages.Builder_Success(String.format("%s/projects/%s", getEffectiveFrontendUrl(), StringUtils.isNotBlank(projectId) ? projectId : StringUtils.EMPTY)));
- updateProjectProperties(logger, apiClient, effectiveProjectName, effectiveProjectVersion);
+ updateProjectProperties(logger, apiClient, effectiveProjectName, effectiveProjectVersion, effectiveProjectProperties);
final var thresholds = getThresholds();
if (synchronous && StringUtils.isNotBlank(uploadResult.getToken())) {
@@ -609,7 +611,7 @@ private Thresholds getThresholds() {
return thresholds;
}
- private void updateProjectProperties(final ConsoleLogger logger, final ApiClient apiClient, final String effectiveProjectName, final String effectiveProjectVersion) throws ApiClientException {
+ private void updateProjectProperties(final ConsoleLogger logger, final ApiClient apiClient, final String effectiveProjectName, final String effectiveProjectVersion, final ProjectProperties effectiveProjectProperties) throws ApiClientException {
// check whether there are settings other than those of the parent project.
// the parent project is set during upload.
boolean doUpdateProject = projectProperties != null && ( // noformat
@@ -621,7 +623,7 @@ private void updateProjectProperties(final ConsoleLogger logger, final ApiClient
if (doUpdateProject) {
logger.log(Messages.Builder_Project_Update());
final String id = lookupProjectId(logger, apiClient, effectiveProjectName, effectiveProjectVersion);
- apiClient.updateProjectProperties(id, projectProperties);
+ apiClient.updateProjectProperties(id, effectiveProjectProperties);
}
}
@@ -636,4 +638,19 @@ private String lookupProjectId(final ConsoleLogger logger, final ApiClient apiCl
}
return projectIdCache;
}
+
+ private ProjectProperties expandProjectProperties(final EnvVars env) {
+ if (projectProperties != null) {
+ final var expandedProperties = new ProjectProperties();
+ Optional.ofNullable(projectProperties.getDescription()).map(env::expand).ifPresent(expandedProperties::setDescription);
+ Optional.ofNullable(projectProperties.getGroup()).map(env::expand).ifPresent(expandedProperties::setGroup);
+ Optional.ofNullable(projectProperties.getParentId()).map(env::expand).ifPresent(expandedProperties::setParentId);
+ Optional.ofNullable(projectProperties.getParentName()).map(env::expand).ifPresent(expandedProperties::setParentName);
+ Optional.ofNullable(projectProperties.getParentVersion()).map(env::expand).ifPresent(expandedProperties::setParentVersion);
+ Optional.ofNullable(projectProperties.getSwidTagId()).map(env::expand).ifPresent(expandedProperties::setSwidTagId);
+ expandedProperties.setTags(projectProperties.getTags().stream().map(env::expand).collect(Collectors.toList()));
+ return expandedProperties;
+ }
+ return null;
+ }
}
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description.html
index e89bacb5..b1d6e67c 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description.html
@@ -1,3 +1,4 @@
The description to be set for the project.
+
The value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description_de.html
index efd0c88e..918a51af 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-description_de.html
@@ -1,3 +1,4 @@
Die Beschreibung, die für das Projekt gesetzt werden soll.
+
Der Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group.html
index db03b758..3f25c014 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group.html
@@ -1,3 +1,4 @@
Specifies the value of "Namespace / Group / Vendor" to be set for the project.
+
The value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group_de.html
index dde5b454..6db920c8 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-group_de.html
@@ -1,3 +1,4 @@
Wert für "Namensraum / Gruppe / Hersteller", der für das Projekt gesetzt werden soll.
+
Der Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId.html
index 10f6cab2..d5267bcb 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId.html
@@ -1,3 +1,4 @@
The ID (UUID) of the parent project.
+
The value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId_de.html
index f23996e7..36d24d38 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentId_de.html
@@ -1,3 +1,4 @@
Die ID (UUID) des übergeordneten Projekts.
+
Der Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName.html
index b1e95159..b3bb46bf 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName.html
@@ -1,3 +1,4 @@
The name of the parent project.
+
The value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName_de.html
index 3425a878..f35458e6 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentName_de.html
@@ -1,3 +1,4 @@
Der Name des übergeordneten Projekts.
+
Der Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion.html
index d3a732b1..29b14070 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion.html
@@ -1,3 +1,4 @@
The version of the parent project.
+
The value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion_de.html
index a7240e60..c9f7ca3f 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-parentVersion_de.html
@@ -1,3 +1,4 @@
Die Version des übergeordneten Projekts.
+
Der Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId.html
index f49ab9f2..5a3315e8 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId.html
@@ -1,3 +1,4 @@
Specifies the SWID Tag ID to be set for the project.
+
The value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId_de.html
index 5be64b82..baef5bb8 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-swidTagId_de.html
@@ -1,3 +1,4 @@
Wert für "SWID Tag ID", der für das Projekt gesetzt werden soll.
+
Der Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags.html
index 6f1090bd..05143cd1 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags.html
@@ -1,4 +1,5 @@
Specifies the list of tags to be set for the project. Separate multiple tags with spaces or put each tag on a separate line.
All tags are automatically lowercased!
+
The tag-value can contain environment variables in the form of ${VARIABLE_NAME}
which are resolved.
diff --git a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags_de.html b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags_de.html
index cb6d430e..a5c79995 100644
--- a/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags_de.html
+++ b/src/main/resources/org/jenkinsci/plugins/DependencyTrack/ProjectProperties/help-tags_de.html
@@ -1,4 +1,5 @@
Gibt die Liste der Tags an, die für das Projekt gesetzt werden sollen. Trennen Sie mehrere Tags durch Leerzeichen oder eine eigene Zeile.
Alle Tags werden automatisch kleingeschrieben!
+
Der Tag-Wert kann Umgebungsvariablen in Form von ${VARIABLE_NAME}
enthalten, die aufgelöst werden.
diff --git a/src/test/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisherTest.java b/src/test/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisherTest.java
index 4e81fd52..78c4cff7 100644
--- a/src/test/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisherTest.java
+++ b/src/test/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisherTest.java
@@ -49,6 +49,7 @@
import org.junit.jupiter.api.io.TempDir;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@@ -173,7 +174,7 @@ void testPerformAsync(@TempDir Path tmpWork) throws IOException {
uut.setProjectProperties(props);
uut.setUnstableTotalCritical(1);
- when(client.upload(eq("uuid-1"), isNull(), isNull(), any(FilePath.class), eq(false), eq(props)))
+ when(client.upload(eq("uuid-1"), isNull(), isNull(), any(FilePath.class), eq(false), any(ProjectProperties.class)))
.thenReturn(new UploadResult(true))
.thenReturn(new UploadResult(false));
@@ -314,7 +315,13 @@ void testPerformSyncWithoutProjectId(@TempDir Path tmpWork) throws IOException {
tmp.createNewFile();
FilePath workDir = new FilePath(tmpWork.toFile());
final var props = new ProjectProperties();
- props.setDescription("description");
+ props.setDescription("description. ${my.var}");
+ props.setTags(List.of("tag1", "${my.var}"));
+ props.setSwidTagId("swidTagId. ${my.var}");
+ props.setGroup("group. ${my.var}");
+ props.setParentId("parentId. ${my.var}");
+ props.setParentName("parentName. ${my.var}");
+ props.setParentVersion("parentVersion. ${my.var}");
DependencyTrackPublisher uut = new DependencyTrackPublisher(tmp.getName(), true, clientFactory);
uut.setProjectName("name-1");
uut.setProjectVersion("version-1");
@@ -323,7 +330,7 @@ void testPerformSyncWithoutProjectId(@TempDir Path tmpWork) throws IOException {
uut.setProjectProperties(props);
final var team = Team.builder().name("test-team").permissions(Set.of(VIEW_POLICY_VIOLATION.toString())).build();
- when(client.upload(isNull(), eq("name-1"), eq("version-1"), any(FilePath.class), eq(true), eq(props))).thenReturn(new UploadResult(true, "token-1"));
+ when(client.upload(isNull(), eq("name-1"), eq("version-1"), any(FilePath.class), eq(true), any(ProjectProperties.class))).thenReturn(new UploadResult(true, "token-1"));
when(client.isTokenBeingProcessed("token-1")).thenReturn(Boolean.TRUE).thenReturn(Boolean.FALSE);
when(client.getFindings("uuid-1")).thenReturn(List.of());
when(client.getTeamPermissions()).thenReturn(team);
@@ -336,7 +343,15 @@ void testPerformSyncWithoutProjectId(@TempDir Path tmpWork) throws IOException {
verify(client).getFindings("uuid-1");
verify(client).getTeamPermissions();
verify(client).getViolations("uuid-1");
- verify(client).updateProjectProperties("uuid-1", props);
+ var propsCaptor = ArgumentCaptor.forClass(ProjectProperties.class);
+ verify(client).updateProjectProperties(eq("uuid-1"), propsCaptor.capture());
+ assertThat(propsCaptor.getValue().getDescription()).isEqualTo("description. my.value");
+ assertThat(propsCaptor.getValue().getTags()).containsExactlyInAnyOrder("tag1", "my.value");
+ assertThat(propsCaptor.getValue().getSwidTagId()).isEqualTo("swidTagId. my.value");
+ assertThat(propsCaptor.getValue().getGroup()).isEqualTo("group. my.value");
+ assertThat(propsCaptor.getValue().getParentId()).isEqualTo("parentId. my.value");
+ assertThat(propsCaptor.getValue().getParentName()).isEqualTo("parentName. my.value");
+ assertThat(propsCaptor.getValue().getParentVersion()).isEqualTo("parentVersion. my.value");
verify(client).lookupProject("name-1", "version-1");
}