Skip to content

Commit

Permalink
Maven - Update dependency versions in the properties section (#365)
Browse files Browse the repository at this point in the history
  • Loading branch information
Or-Geva authored Oct 6, 2023
1 parent fe8b049 commit 23220bb
Show file tree
Hide file tree
Showing 18 changed files with 509 additions and 46 deletions.
130 changes: 126 additions & 4 deletions src/main/dependencyUpdate/mavenDependencyUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import * as fs from 'fs';
import { MavenUtils } from '../utils/mavenUtils';
import { AbstractDependencyUpdate } from './abstractDependencyUpdate';
import { ScanUtils } from '../utils/scanUtils';
import { PackageType } from '../types/projectType';
import { DependencyIssuesTreeNode } from '../treeDataProviders/issuesTree/descriptorTree/dependencyIssuesTreeNode';
import { FileTreeNode } from '../treeDataProviders/issuesTree/fileTreeNode';

/**
* Represents a Maven dependency update implementation.
*/
export class MavenDependencyUpdate extends AbstractDependencyUpdate {
constructor() {
super(PackageType.Maven);
Expand All @@ -16,11 +21,128 @@ export class MavenDependencyUpdate extends AbstractDependencyUpdate {

/** @override */
public update(dependency: DependencyIssuesTreeNode, version: string): void {
const workspace: string = dependency.getDependencyProjectPath();
const [cmd, location] = this.buildUpdateCmd(dependency, version);
ScanUtils.executeCmd(cmd, location);
}

/**
* Builds the Maven update command based on the provided parameters.
* @param dependency The dependency to update.
* @param newVersion The new version to set.
* @returns The Maven update command.
**/
private buildUpdateCmd(dependency: DependencyIssuesTreeNode, newVersion: string): [string, string] {
const version: string | undefined = this.getDependencyVersionFromPom(dependency);
if (!version) {
throw new Error('Failed to find dependency version in pom.xml');
}
if (this.isPropertyVersion(version)) {
const prop: string = this.cleanPropertyVersion(version);
return [this.buildUpdatePropertyCmd(newVersion, prop), this.searchPropertyFilePath(dependency, prop)];
}
return [this.buildUpdateVersionCmd(dependency, newVersion), dependency.getDependencyProjectPath()];
}

private isPropertyVersion(version: string): boolean {
return version.trimStart().startsWith('${');
}

/**
* Cleans the version property from the evaluation tag.
* @param version The version property to clean.
* @returns The cleaned version property.
*/
private cleanPropertyVersion(version: string) {
return version.substring(version.indexOf('${') + 2, version.indexOf('}'));
}

private getDependencyVersionFromPom(dependency: DependencyIssuesTreeNode): string | undefined {
const [groupId, artifactId] = MavenUtils.getGavArray(dependency);
ScanUtils.executeCmd(
'mvn versions:use-dep-version -DgenerateBackupPoms=false -Dincludes=' + groupId + ':' + artifactId + ' -DdepVersion=' + version,
workspace
const pomXmlContent: string = fs.readFileSync(dependency.getDependencyFilePath(), 'utf-8');
return this.getVersionProperty(MavenUtils.getDependencyXmlTag(pomXmlContent, groupId, artifactId));
}

/**
* Searches for the version property declaration in the POM files.
* @param dependency The dependency to search the version property for.
* @param propName The property name to search.
* @returns The path to the POM file where the property is declared.
**/
private searchPropertyFilePath(dependency: DependencyIssuesTreeNode, propName: string) {
return this.searchPropDeclareFile(
// If the there are multi pom project, search in all of them.
// If not, search in the current pom.
dependency.parent.parent ? dependency.parent.parent.children : [dependency.parent],
propName,
dependency.version
);
}

/**
* Searches for the property declaration in the POM files.
* @param files The POM files to search in.
* @param propName The property name to search.
* @param propVersion The property version to search.
* @returns The path to the POM file where the property is declared.
**/
private searchPropDeclareFile(files: FileTreeNode[], propName: string, propVersion: string): string {
for (let mavenProject of files) {
const pomXmlContent: string = fs.readFileSync(mavenProject.projectFilePath, 'utf-8');
if (this.matchProperty(pomXmlContent, propName, propVersion)) {
return mavenProject.getProjectPath();
}
}
throw Error('Failed to find property declaration in pom.xml');
}

/**
* Matches the version property from the XML tag.
* @param xmlTag The XML tag to match the version property from.
* @returns The matched version property, if found.
*/
private getVersionProperty(xmlTag: string): string | undefined {
return xmlTag.match(/<version>(.*)<\/version>/)?.[1];
}

/**
* Matches the property from the POM text.
* @param pomText The POM text to match the property from.
* @param propertyName The property name to match.
* @param propertyVersion The property version to match.
* @returns The matched property, if found.
**/
private matchProperty(pomText: string, propertyName: string, propertyVersion: string): string | undefined {
// Create a regular expression pattern to match the property
const pattern: RegExp = new RegExp(`<${propertyName}>\\s*(${propertyVersion})\\s*</${propertyName}>`, 'i');

// Use the regular expression to find a match in the POM text
const match: RegExpMatchArray | null = pomText.match(pattern);
if (match && match[1]) {
return match[1];
} else {
return undefined;
}
}

/**
* Builds the Maven update command based on the provided parameters.
* @param groupId The group ID of the dependency.
* @param artifactId The artifact ID of the dependency.
* @param newVersion The new version to set.
* @param currentProperty The version property to update if exists.
*/
private buildUpdatePropertyCmd(newVersion: string, propertyName: string) {
return 'mvn versions:set-property -DgenerateBackupPoms=false -DnewVersion=' + newVersion + ' -Dproperty=' + propertyName;
}

/**
* Builds the Maven update command based on the provided parameters.
* @param dependency The dependency to update.
* @param newVersion The new version to set.
* @returns The Maven update command.
**/
private buildUpdateVersionCmd(dependency: DependencyIssuesTreeNode, newVersion: string) {
const [groupId, artifactId] = MavenUtils.getGavArray(dependency);
return 'mvn versions:use-dep-version -DgenerateBackupPoms=false -Dincludes=' + groupId + ':' + artifactId + ' -DdepVersion=' + newVersion;
}
}
1 change: 0 additions & 1 deletion src/main/diagnostics/codeFileActionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { CodeIssueTreeNode } from '../treeDataProviders/issuesTree/codeFileTree/
import { FileTreeNode } from '../treeDataProviders/issuesTree/fileTreeNode';
import { IssueTreeNode } from '../treeDataProviders/issuesTree/issueTreeNode';
import { Severity, SeverityUtils } from '../types/severity';

import { AbstractFileActionProvider } from './abstractFileActionProvider';

export class CodeFileActionProvider extends AbstractFileActionProvider implements vscode.CodeActionProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CveTreeNode } from './cveTreeNode';
import { LicenseIssueTreeNode } from './licenseIssueTreeNode';
import { ContextKeys } from '../../../constants/contextKeys';
import { ProjectDependencyTreeNode } from './projectDependencyTreeNode';
import { IssuesRootTreeNode } from '../issuesRootTreeNode';

export class DependencyIssuesTreeNode extends vscode.TreeItem {
// Infer from data
Expand Down Expand Up @@ -127,6 +128,10 @@ export class DependencyIssuesTreeNode extends vscode.TreeItem {
return this.parent.getProjectPath();
}

public getDependencyRootProject(): IssuesRootTreeNode | undefined {
return this.parent.parent;
}

public getDependencyFilePath(): string {
return this.parent.getProjectFilePath();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { PackageType } from '../../../types/projectType';
import { IssueTreeNode } from '../issueTreeNode';
import { CveTreeNode } from './cveTreeNode';
import { IComponent } from 'jfrog-client-js';
import * as path from 'path';
import { Utils } from '../../../utils/utils';
import { CveApplicableDetails } from '../../../scanLogic/scanRunners/applicabilityScan';

Expand All @@ -24,12 +23,10 @@ export class ProjectDependencyTreeNode extends FileTreeNode {
protected _applicableScanTimeStamp?: number;

protected _packageType: PackageType;
private projectPath: string;

constructor(filePath: string, packageType?: PackageType, parent?: IssuesRootTreeNode) {
super(filePath, parent);
this._packageType = packageType ?? PackageType.Unknown;
this.projectPath = path.dirname(filePath);
}

/** @override */
Expand Down Expand Up @@ -148,10 +145,6 @@ export class ProjectDependencyTreeNode extends FileTreeNode {
return this._packageType;
}

public getProjectPath() {
return this.projectPath;
}

public getProjectFilePath() {
return this.projectFilePath;
}
Expand Down
7 changes: 6 additions & 1 deletion src/main/treeDataProviders/issuesTree/fileTreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import { IssueTreeNode } from './issueTreeNode';
export abstract class FileTreeNode extends vscode.TreeItem {
protected _severity: Severity = Severity.Unknown;
private _name: string;
protected projectPath: string;

constructor(private _fullPath: string, private _parent?: IssuesRootTreeNode, private _timeStamp?: number) {
super(_fullPath);
this._name = Utils.getLastSegment(_fullPath);
this.label = this._name;

this.projectPath = path.dirname(_fullPath);
this.contextValue += ContextKeys.COPY_TO_CLIPBOARD_ENABLED;
}

Expand Down Expand Up @@ -140,4 +141,8 @@ export abstract class FileTreeNode extends vscode.TreeItem {
public set projectFilePath(value: string) {
this._fullPath = value;
}

public getProjectPath() {
return this.projectPath;
}
}
17 changes: 5 additions & 12 deletions src/main/utils/mavenUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ export class MavenUtils {
let res: vscode.Range[] = [];
let pomXmlContent: string = document.getText();
let [groupId, artifactId, version] = MavenUtils.getGavArrayFromId(dependencyId);
let dependencyTag: string = MavenUtils.getDependencyTag(pomXmlContent, groupId, artifactId);
if (dependencyTag) {
let startIndex: vscode.Position = document.positionAt(pomXmlContent.indexOf(dependencyTag));
let arr: string[] = dependencyTag.split(/\r?\n/).filter(line => line.trim() !== '');
let tag: string = MavenUtils.getDependencyXmlTag(pomXmlContent, groupId, artifactId);
if (tag) {
let startIndex: vscode.Position = document.positionAt(pomXmlContent.indexOf(tag));
let arr: string[] = tag.split(/\r?\n/).filter(line => line.trim() !== '');
for (let i: number = 0; i < arr.length; i++) {
let depInfo: string = arr[i].trim().toLowerCase();
if (this.isDependencyMatch(groupId, artifactId, version, depInfo, focusType)) {
Expand Down Expand Up @@ -82,7 +82,7 @@ export class MavenUtils {
* @param groupId - The dependency's group ID
* @param artifactId - The dependency's artifact ID
*/
public static getDependencyTag(pomXmlContent: string, groupId: string, artifactId: string): string {
public static getDependencyXmlTag(pomXmlContent: string, groupId: string, artifactId: string): string {
let groupIdRegex: RegExp = new RegExp(`<groupId>\\s*${groupId}\\s*</groupId>`, 'gi');
let artifactIdRegex: RegExp = new RegExp(`<artifactId>\\s*${artifactId}\\s*</artifactId>`, 'gi');
let dependencyMatch: string[] | undefined = pomXmlContent
Expand Down Expand Up @@ -368,13 +368,6 @@ export class MavenUtils {
return pomTreeArray.findIndex(pomTree => pomTree.pomGav === pomGav);
}

/**
* @param rawDependency Raw dependency text
*/
public static getProjectInfo(rawDependency: string): [string, string, string, string] {
return MavenUtils.getDependencyInfo(rawDependency.replace(/\s/g, '') + ':dummyScope');
}

/**
* @param rawDependency - e.g. "| | +- javax.mail:mail:jar:1.4:compile"
* @returns [groupId,ArtifactId,version]
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jfrog.test</groupId>
<artifactId>multi</artifactId>
<version>3.7-SNAPSHOT</version>
</parent>

<artifactId>multi1</artifactId>
<packaging>jar</packaging>
<name>Multi 1</name>

<licenses>
<license>
<name>apache</name>
<comments>none</comments>
</license>
</licenses>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- deploy test jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>${lets.go.and.fix.this}</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.5.6</version>
</dependency>

<!-- dependency with classifier -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<classifier>jdk15</classifier>
<version>5.9</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Loading

0 comments on commit 23220bb

Please sign in to comment.