Skip to content

Local JAR Support #20

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

Merged
merged 5 commits into from
May 28, 2021
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ build/
.classpath
.settings/
bin/

# IntelliJ
.idea/
out/
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Support for local JAR file ([#20](https://github.com/diffplug/blowdryer/pull/20))

## [1.1.1] - 2021-02-12
### Fixed
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id 'com.diffplug.spotless-changelog'
id 'com.gradle.plugin-publish'
id 'com.jfrog.bintray'
id "com.palantir.idea-test-fix" version "0.1.0" // Added to run tests successfully in IntelliJ
}

apply from: 干.file('base/changelog.gradle')
Expand Down
43 changes: 40 additions & 3 deletions src/main/java/com/diffplug/blowdryer/Blowdryer.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
import com.diffplug.common.hash.Hashing;
import com.diffplug.common.io.Files;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
Expand All @@ -34,6 +37,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
Expand All @@ -47,6 +52,10 @@
* determined by {@link BlowdryerSetup}.
*/
public class Blowdryer {

private static final String FILE_PROTOCOL = "file:///";
private static final String JAR_FILE_RESOURCE_SEPARATOR = "!/";

private Blowdryer() {}

static {
Expand Down Expand Up @@ -111,7 +120,7 @@ public static File immutableUrl(String url) {
urlToContent.put(url, dataFile);
return dataFile;
}
} catch (IOException e) {
} catch (IOException | URISyntaxException e) {
throw Errors.asRuntime(e);
}
}
Expand All @@ -131,7 +140,35 @@ private static Map<String, String> loadPropertyFile(File file) throws IOExceptio

private static final String PROP_URL = "url";

private static void download(String url, File dst) throws IOException {
private static void download(String url, File dst) throws IOException, URISyntaxException {
if (url.startsWith(FILE_PROTOCOL)) {
downloadLocal(url, dst);
} else {
downloadRemote(url, dst);
}
}

private static void downloadLocal(String url, File dst) throws IOException, URISyntaxException {

String[] splitUrl = url.split(JAR_FILE_RESOURCE_SEPARATOR);
if (splitUrl.length != 2) {
throw new IllegalArgumentException("Expected a file URL in the format: file:///path-to-dependency.jar!/path-to-file.ext");
}

String jarPath = splitUrl[0];
String filename = splitUrl[1];

URI jarPathUri = new URI(jarPath);
try (ZipFile jar = new ZipFile(new File(jarPathUri))) {
ZipEntry foundEntry = jar.stream()
.filter(s -> s.getName().equals(filename)).findAny()
.orElseThrow(() -> new FileNotFoundException("Could not find '" + filename + "' in '" + jarPath + "'"));

java.nio.file.Files.copy(jar.getInputStream(foundEntry), dst.toPath());
}
}

private static void downloadRemote(String url, File dst) throws IOException {
OkHttpClient client = new OkHttpClient.Builder().build();
Request.Builder req = new Request.Builder().url(url);
authPlugin.addAuthToken(url, req);
Expand All @@ -154,7 +191,7 @@ private static void download(String url, File dst) throws IOException {

/** Returns either the filename safe URL, or (first40)--(Base64 filenamesafe)(last40). */
static String filenameSafe(String url) {
String allSafeCharacters = url.replaceAll("[^a-zA-Z0-9-+_\\.]", "-");
String allSafeCharacters = url.replaceAll("[^a-zA-Z0-9-+_.]", "-");
String noDuplicateDash = allSafeCharacters.replaceAll("-+", "-");
if (noDuplicateDash.length() <= MAX_FILE_LENGTH) {
return noDuplicateDash;
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/diffplug/blowdryer/BlowdryerSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ private GitLab setGlobals() {
}
}

/**
* Uses the provided {@code jarFile} to extract a file resource.
* @param jarFile Absolute path to JAR on the file system.
*/
public void localJar(File jarFile) {
Objects.requireNonNull(jarFile, "jarFile must not be null.");
Blowdryer.setResourcePluginNull();

String rootUrl = "file:///" + jarFile.getAbsolutePath() + "!/";
Blowdryer.setResourcePlugin(resource -> rootUrl + resource);
}

@NotNull
private String getFullResourcePath(String resource) {
return (repoSubfolder.isEmpty() ? "" : repoSubfolder + "/") + resource;
Expand Down
80 changes: 57 additions & 23 deletions src/test/java/com/diffplug/blowdryer/BlowdryerPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,46 +23,56 @@
import org.junit.Test;

public class BlowdryerPluginTest extends GradleHarness {

private static final String SETTINGS_GRADLE = "settings.gradle";
private static final String BUILD_GRADLE = "build.gradle";

private void settingsGithub(String tag, String... extra) throws IOException {
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { github('diffplug/blowdryer', 'tag', '" + tag + "') }",
Arrays.stream(extra).collect(Collectors.joining("\n")));
}

private void settingsGitlab(String tag, String... extra) throws IOException {
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { gitlab('diffplug/blowdryer', 'tag', '" + tag + "') }",
Arrays.stream(extra).collect(Collectors.joining("\n")));
}

private void settingsCustomGitlab(String tag, String... extra) throws IOException {
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { gitlab('diffplug/blowdryer', 'tag', '" + tag + "').customDomainHttps('gitlab.com') }",
Arrays.stream(extra).collect(Collectors.joining("\n")));
}

private void settingsGitlabRootFolder(String tag, String... extra) throws IOException {
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { repoSubfolder(''); gitlab('diffplug/blowdryer', 'tag', '" + tag + "') }",
Arrays.stream(extra).collect(Collectors.joining("\n")));
}

private void settingsLocalJar(String dependency) throws IOException {
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { localJar(file('" + dependency + "')) }");
}

@Test
public void githubTag() throws IOException {
settingsGithub("test/2/a");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'a'",
"assert 干.prop('sample', 'name') == 'test'",
"assert 干.prop('sample', 'ver_spotless') == '1.2.0'");
gradleRunner().build();

settingsGithub("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'b'",
"assert 干.prop('sample', 'name') == 'testB'",
Expand All @@ -71,7 +81,7 @@ public void githubTag() throws IOException {

// double-check that failures do fail
settingsGithub("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"plugins { id 'com.diffplug.blowdryer' }",
"assert Blowdryer.file('sample').text == 'a'");
gradleRunner().buildAndFail();
Expand All @@ -80,15 +90,15 @@ public void githubTag() throws IOException {
@Test
public void gitlabTag() throws IOException {
settingsGitlab("test/2/a");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'a'",
"assert 干.prop('sample', 'name') == 'test'",
"assert 干.prop('sample', 'ver_spotless') == '1.2.0'");
gradleRunner().build();

settingsGitlab("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'b'",
"assert 干.prop('sample', 'name') == 'testB'",
Expand All @@ -97,7 +107,7 @@ public void gitlabTag() throws IOException {

// double-check that failures do fail
settingsGitlab("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"plugins { id 'com.diffplug.blowdryer' }",
"assert Blowdryer.file('sample').text == 'a'");
gradleRunner().buildAndFail();
Expand All @@ -106,15 +116,15 @@ public void gitlabTag() throws IOException {
@Test
public void customGitlabTag() throws IOException {
settingsCustomGitlab("test/2/a");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'a'",
"assert 干.prop('sample', 'name') == 'test'",
"assert 干.prop('sample', 'ver_spotless') == '1.2.0'");
gradleRunner().build();

settingsCustomGitlab("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'b'",
"assert 干.prop('sample', 'name') == 'testB'",
Expand All @@ -123,7 +133,7 @@ public void customGitlabTag() throws IOException {

// double-check that failures do fail
settingsCustomGitlab("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"plugins { id 'com.diffplug.blowdryer' }",
"assert Blowdryer.file('sample').text == 'a'");
gradleRunner().buildAndFail();
Expand All @@ -132,15 +142,15 @@ public void customGitlabTag() throws IOException {
@Test
public void rootfolderGitlabTag() throws IOException {
settingsGitlabRootFolder("test/2/a");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('src/main/resources/sample').text == 'a'",
"assert 干.prop('src/main/resources/sample', 'name') == 'test'",
"assert 干.prop('src/main/resources/sample', 'ver_spotless') == '1.2.0'");
gradleRunner().build();

settingsGitlabRootFolder("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('src/main/resources/sample').text == 'b'",
"assert 干.prop('src/main/resources/sample', 'name') == 'testB'",
Expand All @@ -149,7 +159,7 @@ public void rootfolderGitlabTag() throws IOException {

// double-check that failures do fail
settingsGitlabRootFolder("test/2/b");
write("build.gradle",
write(BUILD_GRADLE,
"plugins { id 'com.diffplug.blowdryer' }",
"assert Blowdryer.file('src/main/resources/sample').text == 'a'");
gradleRunner().buildAndFail();
Expand All @@ -161,10 +171,10 @@ public void devLocal() throws IOException {
write("../blowdryer-script/src/main/resources/sample.properties",
"name=test",
"group=com.diffplug.gradle");
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { devLocal('../blowdryer-script') }");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
// .replace('\\r', '') fixes test on windows
"assert 干.file('sample').text.replace('\\r', '') == 'c\\n'",
Expand All @@ -177,7 +187,7 @@ public void devLocal() throws IOException {
public void multiproject() throws IOException {
settingsGithub("test/2/a",
"include 'subproject'");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').text == 'a'",
"assert 干.prop('sample', 'name') == 'test'",
Expand All @@ -199,7 +209,7 @@ public void multiproject() throws IOException {
@Test
public void missingResourceThrowsError() throws IOException {
settingsGithub("test/2/a");
write("build.gradle",
write(BUILD_GRADLE,
"plugins { id 'com.diffplug.blowdryer' }",
"干.file('notPresent')");
Assertions.assertThat(gradleRunner().buildAndFail().getOutput().replace("\r\n", "\n")).contains(
Expand All @@ -213,7 +223,7 @@ public void cfgTestGroovy() throws IOException {
write("../blowdryer-script/src/main/resources/sample.properties",
"name=test",
"group=com.diffplug.gradle");
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { devLocal('../blowdryer-script') }");
write("../blowdryer-script/src/main/resources/script.gradle",
Expand All @@ -223,7 +233,7 @@ public void cfgTestGroovy() throws IOException {
"println 干.proj(File.class, 'keyFile', 'location of the keyFile')",
"println 干.prop('sample', 'group')",
"");
write("build.gradle",
write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"ext.pluginPass = 'supersecret'",
"ext.keyFile = new File('keyFile.txt')",
Expand Down Expand Up @@ -267,7 +277,7 @@ public void cfgTestKotlin() throws IOException {

@Test
public void settingsTest() throws IOException {
write("settings.gradle",
write(SETTINGS_GRADLE,
"plugins { id 'com.diffplug.blowdryerSetup' }",
"blowdryerSetup { github('diffplug/blowdryer', 'tag', 'test/2/a') }",
"import com.diffplug.blowdryer.干",
Expand All @@ -277,4 +287,28 @@ public void settingsTest() throws IOException {
"println 'test was success'");
Assertions.assertThat(gradleRunner().build().getOutput().replace("\r\n", "\n"));
}

@Test
public void localJarFileDownloadExists() throws IOException {
String jarFile = BlowdryerPluginTest.class.getResource("test.jar").getFile();
settingsLocalJar(jarFile);

write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('sample').exists()");

gradleRunner().build();
}

@Test
public void localJarFileDownloadDoesNotExist() throws IOException {
String jarFile = BlowdryerPluginTest.class.getResource("test.jar").getFile();
settingsLocalJar(jarFile);

write(BUILD_GRADLE,
"apply plugin: 'com.diffplug.blowdryer'",
"assert 干.file('invalid-file.txt').exists()");

gradleRunner().buildAndFail();
}
}
9 changes: 9 additions & 0 deletions src/test/java/com/diffplug/blowdryer/BlowdryerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import org.junit.Test;

public class BlowdryerTest {
private static final String JAR_FILE_RESOURCE_SEPARATOR = "!/";
private static final String FILE_PROTOCOL = "file:///";

@Test
public void filenameSafe() {
filenameSafe("http://shortName.com/a+b-0-9~Z", "http-shortName.com-a+b-0-9-Z");
Expand All @@ -38,4 +41,10 @@ public void cachedFileDeleted_issue_11() {
Blowdryer.immutableUrl(test).delete();
Assertions.assertThat(Blowdryer.immutableUrl(test)).hasContent("b");
}

@Test
public void immutableUrlOfLocalJar() {
String jarFile = BlowdryerPluginTest.class.getResource("test.jar").getFile();
Assertions.assertThat(Blowdryer.immutableUrl(FILE_PROTOCOL + jarFile + JAR_FILE_RESOURCE_SEPARATOR + "sample")).exists();
}
}
Binary file added src/test/resources/com/diffplug/blowdryer/test.jar
Binary file not shown.