Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
### ⚠ Breaking
- require Jenkins 2.479.1 or newer
- require Java 17 or newer (required since Jenkins 2.479.1)
- require Dependency-Track 4.12 or newer ([#286](https://github.com/jenkinsci/dependency-track-plugin/issues/286))

### ⭐ New Features
- Support "isLatest" flag ([#286](https://github.com/jenkinsci/dependency-track-plugin/issues/286))

### 🐞 Bugs Fixed

## v5.2.0 - 2024-12-08
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Asynchronous publishing simply uploads the SBOM to Dependency-Track and the job
![build summary](docs/images/jenkins-build-summary.png)
![findings](docs/images/jenkins-build-findings.png) ![policy violations](docs/images/jenkins-build-policy-violations.png)

## Version Compatibility Matrix
Plugin Version | Dependency-Track | Jenkins | Java
---------------| ---------------- | ------- | ----
6.0.x (next) | 4.12+ | 2.479.1+ | 17+
5.2.x (current) | 4.9+ | 2.440.1+ | 11+

## Global Configuration
To setup, navigate to Jenkins > System Configuration and complete the Dependency-Track section.

Expand Down Expand Up @@ -75,7 +81,9 @@ Once configured with a valid URL and API key, simply configure a job to publish
- SWID tag ID
- group/vendor
- description
- ID of parent project (for Dependency-Track v4.7 and newer)
- ID of parent project
- name and version of parent project (as an alternative to the ID)
- "is latest version" flag

The use of environment variables in the form `${VARIABLE}` is supported here.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,7 @@ public List<Violation> getViolations(@NonNull final String projectUuid) throws A

@NonNull
@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public UploadResult upload(@Nullable final String projectId, @Nullable final String projectName, @Nullable final String projectVersion, @NonNull final FilePath artifact,
boolean autoCreateProject, @Nullable final ProjectProperties properties) throws ApiClientException {
public UploadResult upload(@NonNull final ProjectData project, @NonNull final FilePath artifact) throws ApiClientException {
final String encodedScan;
try (var in = artifact.read()) {
encodedScan = Base64.getEncoder().encodeToString(in.readAllBytes());
Expand All @@ -303,17 +302,19 @@ public UploadResult upload(@Nullable final String projectId, @Nullable final Str
// Creates the JSON payload that will be sent to Dependency-Track
final var bomSubmitRequest = new JSONObject();
bomSubmitRequest.element("bom", encodedScan);
if (StringUtils.isNotBlank(projectId)) {
bomSubmitRequest.element("project", projectId);
if (StringUtils.isNotBlank(project.id())) {
bomSubmitRequest.element("project", project.id());
} else {
bomSubmitRequest.element("projectName", projectName)
.element("projectVersion", projectVersion)
.element("autoCreate", autoCreateProject);
bomSubmitRequest.element("projectName", project.name())
.element("projectVersion", project.version())
.element("autoCreate", project.autoCreate());
}
final var properties = project.properties();
if (properties != null) {
bomSubmitRequest.elementOpt("parentUUID", properties.getParentId())
.elementOpt("parentName", properties.getParentName())
.elementOpt("parentVersion", properties.getParentVersion());
.elementOpt("parentVersion", properties.getParentVersion())
.elementOpt("isLatestProjectVersion", properties.getIsLatest());
}
final var request = createRequest(URI.create(BOM_URL), "PUT", RequestBody.create(bomSubmitRequest.toString(), APPLICATION_JSON));
return executeWithRetry(() -> {
Expand Down Expand Up @@ -365,14 +366,18 @@ public void updateProjectProperties(@NonNull final String projectUuid, @NonNull
updates.elementOpt("group", properties.getGroup());
// overwrite description only if it is set (means not null)
updates.elementOpt("description", properties.getDescription());
// overwrite isLatest only if it is set (means not null)
updates.elementOpt("isLatest", properties.getIsLatest());
// set new parent project if it is set (means not null)
if (properties.getParentId() != null) {
JSONObject newParent = new JSONObject().elementOpt("uuid", properties.getParentId());
updates.element("parent", newParent);
}

// update project
updateProject(projectUuid, updates);
// update project if necessary
if (!updates.isEmpty()) {
updateProject(projectUuid, updates);
}
}

@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
Expand Down Expand Up @@ -450,4 +455,12 @@ private interface RetryAction<T, E extends IOException> {

T doWithRetry() throws E;
}

static record ProjectData(@Nullable String id,
@Nullable String name,
@Nullable String version,
boolean autoCreate,
@Nullable ProjectProperties properties) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@
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, effectiveProjectProperties);
final var projectData = new ApiClient.ProjectData(projectId, effectiveProjectName, effectiveProjectVersion, effectiveAutocreate, effectiveProjectProperties);
final UploadResult uploadResult = apiClient.upload(projectData, artifactFilePath);

if (!uploadResult.isSuccess()) {
throw new AbortException(Messages.Builder_Upload_Failed());
Expand Down Expand Up @@ -615,6 +615,7 @@
projectProperties.getDescription() != null
|| projectProperties.getGroup() != null
|| projectProperties.getSwidTagId() != null
|| projectProperties.getIsLatest() != null

Check warning on line 618 in src/main/java/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 618 is only partially covered, one branch is missing
|| !projectProperties.getTags().isEmpty());

if (doUpdateProject) {
Expand Down Expand Up @@ -646,6 +647,7 @@
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).toList());
expandedProperties.setIsLatest(projectProperties.getIsLatest());
return expandedProperties;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ private FormValidation testConnection(final String dependencyTrackUrl, final Str
return FormValidation.error(Messages.Publisher_ConnectionTest_Error(poweredBy));
}
final VersionNumber version = apiClient.getVersion();
final var requiredVersion = new VersionNumber("4.9.0");
final var requiredVersion = new VersionNumber("4.12.0");
if (version.isOlderThan(requiredVersion)) {
return FormValidation.error(Messages.Publisher_ConnectionTest_VersionWarning(version, requiredVersion));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import jenkins.model.Jenkins;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
Expand Down Expand Up @@ -93,6 +94,13 @@ public final class ProjectProperties extends AbstractDescribableImpl<ProjectProp
@Nullable
private String parentVersion;

/**
* Mark this version of the project as the latest version
*/
@Nullable
@Setter(onMethod_ = {@DataBoundSetter})
private Boolean isLatest;

@NonNull
public List<String> getTags() {
return normalizeTags(tags);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ limitations under the License.
<f:entry field="description" title="${%description}">
<f:textbox id="description" />
</f:entry>
<f:entry field="isLatest" title="${%isLatest}">
<f:checkbox id="isLatest" />
</f:entry>
<f:entry field="parentId" title="${%parentId}">
<f:select id="parentId"/>
</f:entry>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ group=Namespace / Group / Vendor
description=Description
parentId=Parent project
parentName=Parent name
parentVersion=Parent version
parentVersion=Parent version
isLatest=Is latest version
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ group=Namensraum / Gruppe / Hersteller
description=Beschreibung
parentId=\u00dcbergeordnetes Projekt
parentName=Name des \u00fcbergeordneten Projekts
parentVersion=Version des \u00fcbergeordneten Projekts
parentVersion=Version des \u00fcbergeordneten Projekts
isLatest=Ist aktuellste Version
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
<p>Marks this version of the project as the latest.</p>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
<p>Markiert diese Version des Projekts als die neueste.</p>
</div>
Loading
Loading