chore: initial import
This commit is contained in:
+174
@@ -0,0 +1,174 @@
|
||||
package org.gradle.wrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* Small auditable Gradle wrapper bootstrap used because the official wrapper JAR
|
||||
* could not be generated in the build container. It downloads the configured
|
||||
* distribution, validates SHA-256, extracts it with zip-slip protection and
|
||||
* starts Gradle in the project directory.
|
||||
*/
|
||||
public final class GradleWrapperMain {
|
||||
private GradleWrapperMain() {}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path jar = Paths.get(GradleWrapperMain.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
Path projectRoot = jar.getParent().getParent().getParent();
|
||||
Path propertiesPath = jar.resolveSibling("gradle-wrapper.properties");
|
||||
Properties properties = new Properties();
|
||||
try (InputStream input = Files.newInputStream(propertiesPath)) {
|
||||
properties.load(input);
|
||||
}
|
||||
|
||||
URI distributionUri = URI.create(required(properties, "distributionUrl"));
|
||||
String archiveName = Paths.get(distributionUri.getPath()).getFileName().toString();
|
||||
String distributionName = archiveName.replaceFirst("-(bin|all)\\.zip$", "");
|
||||
Path distributionDirectory = gradleUserHome(args)
|
||||
.resolve(properties.getProperty("distributionPath", "wrapper/dists"))
|
||||
.resolve(distributionName)
|
||||
.resolve(archiveName);
|
||||
Path archive = distributionDirectory.resolve(archiveName);
|
||||
Path marker = distributionDirectory.resolve(".installed");
|
||||
Files.createDirectories(distributionDirectory);
|
||||
|
||||
if (!Files.exists(marker)) {
|
||||
if (!Files.exists(archive)) {
|
||||
download(distributionUri.toURL(), archive, intProperty(properties, "networkTimeout", 10_000));
|
||||
}
|
||||
String expectedSha = properties.getProperty("distributionSha256Sum", "").trim();
|
||||
if (!expectedSha.isEmpty()) {
|
||||
verifySha256(archive, expectedSha);
|
||||
}
|
||||
unzip(archive, distributionDirectory);
|
||||
Files.writeString(marker, "ok\n", StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
Path gradleHome;
|
||||
try (Stream<Path> children = Files.list(distributionDirectory)) {
|
||||
gradleHome = children
|
||||
.filter(Files::isDirectory)
|
||||
.filter(path -> path.getFileName().toString().startsWith("gradle-"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IOException("Extracted Gradle directory not found in " + distributionDirectory));
|
||||
}
|
||||
|
||||
boolean windows = System.getProperty("os.name", "").toLowerCase().contains("win");
|
||||
Path executable = gradleHome.resolve("bin").resolve(windows ? "gradle.bat" : "gradle");
|
||||
if (!windows) executable.toFile().setExecutable(true);
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add(executable.toString());
|
||||
for (String argument : args) command.add(argument);
|
||||
Process process = new ProcessBuilder(command)
|
||||
.directory(projectRoot.toFile())
|
||||
.inheritIO()
|
||||
.start();
|
||||
System.exit(process.waitFor());
|
||||
}
|
||||
|
||||
private static Path gradleUserHome(String[] args) {
|
||||
for (int index = 0; index < args.length - 1; index++) {
|
||||
if ("-g".equals(args[index]) || "--gradle-user-home".equals(args[index])) {
|
||||
return Paths.get(args[index + 1]).toAbsolutePath();
|
||||
}
|
||||
}
|
||||
String configured = System.getenv("GRADLE_USER_HOME");
|
||||
return configured == null || configured.isBlank()
|
||||
? Paths.get(System.getProperty("user.home"), ".gradle")
|
||||
: Paths.get(configured).toAbsolutePath();
|
||||
}
|
||||
|
||||
private static void download(URL initialUrl, Path destination, int timeoutMillis) throws Exception {
|
||||
URL url = initialUrl;
|
||||
for (int redirect = 0; redirect < 8; redirect++) {
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setConnectTimeout(timeoutMillis);
|
||||
connection.setReadTimeout(Math.max(timeoutMillis, 30_000));
|
||||
connection.setRequestProperty("User-Agent", "boehmitools-training-gradle-wrapper");
|
||||
if (connection instanceof HttpURLConnection http) {
|
||||
http.setInstanceFollowRedirects(false);
|
||||
int status = http.getResponseCode();
|
||||
if (status >= 300 && status < 400) {
|
||||
String location = http.getHeaderField("Location");
|
||||
if (location == null) throw new IOException("Redirect without Location from " + url);
|
||||
url = url.toURI().resolve(location).toURL();
|
||||
http.disconnect();
|
||||
continue;
|
||||
}
|
||||
if (status >= 400) throw new IOException("HTTP " + status + " while downloading " + url);
|
||||
}
|
||||
Path partial = destination.resolveSibling(destination.getFileName() + ".part");
|
||||
try (InputStream input = connection.getInputStream(); OutputStream output = Files.newOutputStream(partial)) {
|
||||
input.transferTo(output);
|
||||
}
|
||||
Files.move(partial, destination, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
return;
|
||||
}
|
||||
throw new IOException("Too many redirects while downloading " + initialUrl);
|
||||
}
|
||||
|
||||
private static void verifySha256(Path file, String expected) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (InputStream input = Files.newInputStream(file)) {
|
||||
byte[] buffer = new byte[64 * 1024];
|
||||
for (int read; (read = input.read(buffer)) >= 0;) {
|
||||
if (read > 0) digest.update(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
String actual = HexFormat.of().formatHex(digest.digest());
|
||||
if (!actual.equalsIgnoreCase(expected)) {
|
||||
Files.deleteIfExists(file);
|
||||
throw new IOException("Gradle distribution SHA-256 mismatch: expected " + expected + ", got " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
private static void unzip(Path archive, Path destination) throws IOException {
|
||||
try (ZipInputStream zip = new ZipInputStream(Files.newInputStream(archive))) {
|
||||
for (ZipEntry entry; (entry = zip.getNextEntry()) != null;) {
|
||||
Path target = destination.resolve(entry.getName()).normalize();
|
||||
if (!target.startsWith(destination.normalize())) {
|
||||
throw new IOException("Unsafe ZIP entry: " + entry.getName());
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(target);
|
||||
} else {
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(zip, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
zip.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String required(Properties properties, String key) {
|
||||
String value = properties.getProperty(key);
|
||||
if (value == null || value.isBlank()) throw new IllegalArgumentException("Missing " + key);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int intProperty(Properties properties, String key, int fallback) {
|
||||
try {
|
||||
return Integer.parseInt(properties.getProperty(key, Integer.toString(fallback)));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6
|
||||
Reference in New Issue
Block a user