Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for maven wrapper (mvnw) #50

Merged
merged 1 commit into from
Feb 10, 2020
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
87 changes: 71 additions & 16 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,42 @@ import * as path from 'path';
import * as subProcess from './sub-process';
import { legacyPlugin } from '@snyk/cli-interface';
import { containsJar, createPomForJar, createPomForJars, isJar } from './jar';
import * as os from 'os';

export interface MavenOptions extends legacyPlugin.BaseInspectOptions {
scanAllUnmanaged?: boolean;
}

export function getCommand(root: string, targetFile: string | undefined) {
if (!targetFile) {
sfat marked this conversation as resolved.
Show resolved Hide resolved
return 'mvn';
}
const isWinLocal = /^win/.test(os.platform()); // local check, can be stubbed in tests
const wrapperScript = isWinLocal ? 'mvnw.cmd' : './mvnw';
// try to find a sibling wrapper script first
let pathToWrapper = path.resolve(
root,
path.dirname(targetFile),
wrapperScript,
);
if (fs.existsSync(pathToWrapper)) {
return wrapperScript;
}
// now try to find a wrapper in the root
pathToWrapper = path.resolve(root, wrapperScript);
if (fs.existsSync(pathToWrapper)) {
return wrapperScript;
}
return 'mvn';
}

// When we have `mvn`, we can run the subProcess from anywhere.
// However due to https://github.com/takari/maven-wrapper/issues/133, `mvnw` can only be run
// within the directory where `mvnw` exists
function calculateTargetFilePath(mavenCommand, root: string, targetPath) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orsagie, @lili2311
fyi, this is will be a limitation because of that ticket I mentioned.
Do you think I should also add a line in the README.md?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this what cwd is for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it is, but the problem occurs when you specify a relative path to the targetFile will encounter a error as the mvnw is found, but the pom.xml is not present in that directory.
So, until that is fixed, you will not be able to specify a relative path for the targeFile (../somewhere-else) when mvnw is present.

That's what I meant by limitation.

Because at first I just went ahead and cwd with where the targetFile path is, but then I bumped into issues where the path to the pom.xml would be messed up while subProcess would be ran in both mvn and mvnw cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case let's add a check to see that mvnw is present in the root and propagate an error with instructions on how to properly run if that is not the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will that work if you go with the targetFile approach being outside of that directory where you want to test your project?

return mavenCommand === 'mvn' ? root : path.dirname(targetPath);
}

export async function inspect(
root: string,
targetFile?: string,
Expand Down Expand Up @@ -38,11 +69,23 @@ export async function inspect(
}

const mvnArgs = buildArgs(targetFile, options.args);
const mavenCommand = getCommand(root, targetFile);
const targetFilePath = calculateTargetFilePath(
mavenCommand,
root,
targetPath,
);
try {
sfat marked this conversation as resolved.
Show resolved Hide resolved
const result = await subProcess.execute('mvn', mvnArgs, { cwd: root });
const versionResult = await subProcess.execute('mvn --version', [], {
cwd: root,
const result = await subProcess.execute(mavenCommand, mvnArgs, {
cwd: targetFilePath,
});
const versionResult = await subProcess.execute(
`${mavenCommand} --version`,
[],
{
cwd: targetFilePath,
},
);
const parseResult = parseTree(result, options.dev);
const { javaVersion, mavenVersion } = parseVersions(versionResult);
return {
Expand All @@ -61,19 +104,7 @@ export async function inspect(
package: parseResult.data,
};
} catch (error) {
error.message =
error.message +
'\n\n' +
'Please make sure that Apache Maven Dependency Plugin ' +
'version 2.2 or above is installed, and that ' +
'`mvn ' +
mvnArgs.join(' ') +
'` executes successfully ' +
'on this project.\n\n' +
'If the problem persists, collect the output of ' +
'`mvn ' +
mvnArgs.join(' ') +
'` and contact [email protected]\n';
error.message = buildErrorMessage(error, mvnArgs, mavenCommand);
throw error;
}
}
Expand All @@ -92,3 +123,27 @@ export function buildArgs(
}
return args;
}

function buildErrorMessage(
sfat marked this conversation as resolved.
Show resolved Hide resolved
error: Error,
mvnArgs: string[],
mavenCommand: string,
): string {
const mavenArguments: string = mvnArgs.join(' ');
const fullCommand = `${mavenCommand} ${mavenArguments}`;
const mvnwCommandTipMessage =
'Currently, you cannot run `mvnw` outside your current directory, you will have to go inside the directory of your project (see: https://github.com/takari/maven-wrapper/issues/133)\n\n';
return (
error.message +
'\n\n' +
'Please make sure that Apache Maven Dependency Plugin ' +
'version 2.2 or above is installed, and that `' +
fullCommand +
'` executes successfully ' +
'on this project.\n\n' +
(mavenCommand.indexOf('mvnw') >= 0 ? mvnwCommandTipMessage : '') +
'If the problem persists, collect the output of `' +
fullCommand +
'` and contact [email protected]\n'
);
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.7.0",
"prettier": "^1.19.1",
"@types/sinon": "^7.0.10",
"semantic-release": "^15",
"sinon": "^2.4.1",
"tap": "^12.0.1",
"tap-only": "0.0.5"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;

public class MavenWrapperDownloader {

private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";

/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";

/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";

/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";

public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());

// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);

File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}

private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
Loading