Skip to content

Commit

Permalink
Merge pull request #289 from jenkinsci/feature/support-variables-in-p…
Browse files Browse the repository at this point in the history
…rojectproperties

Support variables in project properties
  • Loading branch information
sephiroth-j authored Nov 28, 2024
2 parents d997849 + 8255bde commit d326796
Show file tree
Hide file tree
Showing 18 changed files with 58 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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())) {
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
}

Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
The description to be set for the project.
<p>The value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Die Beschreibung, die für das Projekt gesetzt werden soll.
<p>Der Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Specifies the value of "Namespace / Group / Vendor" to be set for the project.
<p>The value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Wert für "Namensraum / Gruppe / Hersteller", der für das Projekt gesetzt werden soll.
<p>Der Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
The ID (UUID) of the parent project.
<p>The value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Die ID (UUID) des übergeordneten Projekts.
<p>Der Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
The name of the parent project.
<p>The value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Der Name des übergeordneten Projekts.
<p>Der Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
The version of the parent project.
<p>The value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Die Version des übergeordneten Projekts.
<p>Der Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Specifies the SWID Tag ID to be set for the project.
<p>The value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div>
Wert für "SWID Tag ID", der für das Projekt gesetzt werden soll.
<p>Der Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<div>
Specifies the list of tags to be set for the project. Separate multiple tags with spaces or put each tag on a separate line.
<p>All tags are automatically lowercased!</p>
<p>The tag-value can contain environment variables in the form of <code>${VARIABLE_NAME}</code> which are resolved.</p>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<div>
Gibt die Liste der Tags an, die für das Projekt gesetzt werden sollen. Trennen Sie mehrere Tags durch Leerzeichen oder eine eigene Zeile.
<p>Alle Tags werden automatisch kleingeschrieben!</p>
<p>Der Tag-Wert kann Umgebungsvariablen in Form von <code>${VARIABLE_NAME}</code> enthalten, die aufgelöst werden.</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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");
Expand All @@ -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);
Expand All @@ -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");
}

Expand Down

0 comments on commit d326796

Please sign in to comment.