mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-04-25 07:37:19 -04:00
Add verification metadata for dependencies (#88814)
Removing the custom dependency checksum functionality in favor of Gradle build-in dependency verification support. - Use sha256 in favor of sha1 as sha1 is not considered safe these days. Closes https://github.com/elastic/elasticsearch/issues/69736
This commit is contained in:
parent
1c56d68f49
commit
3909b5eaf9
348 changed files with 3642 additions and 734 deletions
32
BUILDING.md
32
BUILDING.md
|
@ -63,6 +63,38 @@ E.g. [configuration-cache support](https://github.com/elastic/elasticsearch/issu
|
||||||
There are a few guidelines to follow that should make your life easier to make changes to the elasticsearch build.
|
There are a few guidelines to follow that should make your life easier to make changes to the elasticsearch build.
|
||||||
Please add a member of the `es-delivery` team as a reviewer if you're making non-trivial changes to the build.
|
Please add a member of the `es-delivery` team as a reviewer if you're making non-trivial changes to the build.
|
||||||
|
|
||||||
|
#### Adding or updating a dependency
|
||||||
|
|
||||||
|
We rely on [Gradle dependency verification](https://docs.gradle.org/current/userguide/dependency_verification.html) to mitigate the security risks and avoid integrating compromised dependencies.
|
||||||
|
|
||||||
|
This requires to have third party dependencies and their checksums listed in `gradle/verification-metadata.xml`.
|
||||||
|
|
||||||
|
For updated or newly added dependencies you need to add an entry to this verification file or update the existing one:
|
||||||
|
```
|
||||||
|
<component group="asm" name="asm" version="3.1">
|
||||||
|
<artifact name="asm-3.1.jar">
|
||||||
|
<sha256 value="333ff5369043975b7e031b8b27206937441854738e038c1f47f98d072a20437a" origin="official site"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also automate the generation of this entry by running your build using the `--write-verification-metadata` commandline option:
|
||||||
|
```
|
||||||
|
>./gradlew --write-verification-metadata sha256 precommit
|
||||||
|
```
|
||||||
|
|
||||||
|
The `--write-verification-metadata` Gradle option is generally able to resolve reachable configurations,
|
||||||
|
but we use detached configurations for a certain set of plugins and tasks. Therefore, please ensure you run this option with a task that
|
||||||
|
uses the changed dependencies. In most cases, `precommit` or `check` are good candidates.
|
||||||
|
|
||||||
|
We prefer sha256 checksums as md5 and sha1 are not considered safe anymore these days. The generated entry
|
||||||
|
will have the `origin` attribute been set to `Generated by Gradle`.
|
||||||
|
|
||||||
|
>A manual confirmation of the Gradle generated checksums is currently not mandatory.
|
||||||
|
>If you want to add a level of verification you can manually confirm the checksum (e.g by looking it up on the website of the library)
|
||||||
|
>Please replace the content of the `origin` attribute by `official site` in that case.
|
||||||
|
>
|
||||||
|
|
||||||
#### Custom Plugin and Task implementations
|
#### Custom Plugin and Task implementations
|
||||||
|
|
||||||
Build logic that is used across multiple subprojects should considered to be moved into a Gradle plugin with according Gradle task implmentation.
|
Build logic that is used across multiple subprojects should considered to be moved into a Gradle plugin with according Gradle task implmentation.
|
||||||
|
|
|
@ -34,9 +34,6 @@ public class DependencyLicensesPrecommitPlugin extends PrecommitPlugin {
|
||||||
runtimeClasspath.fileCollection(dependency -> dependency instanceof ProjectDependency == false).minus(compileOnly)
|
runtimeClasspath.fileCollection(dependency -> dependency instanceof ProjectDependency == false).minus(compileOnly)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// we also create the updateShas helper task that is associated with dependencyLicenses
|
|
||||||
project.getTasks().register("updateShas", UpdateShasTask.class, t -> t.setParentTask(dependencyLicenses));
|
|
||||||
return dependencyLicenses;
|
return dependencyLicenses;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.gradle.internal.precommit;
|
package org.elasticsearch.gradle.internal.precommit;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Hex;
|
|
||||||
import org.elasticsearch.gradle.internal.precommit.LicenseAnalyzer.LicenseInfo;
|
import org.elasticsearch.gradle.internal.precommit.LicenseAnalyzer.LicenseInfo;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.GradleException;
|
import org.gradle.api.GradleException;
|
||||||
|
@ -23,30 +22,21 @@ import org.gradle.api.provider.Provider;
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
import org.gradle.api.tasks.InputDirectory;
|
import org.gradle.api.tasks.InputDirectory;
|
||||||
import org.gradle.api.tasks.InputFiles;
|
import org.gradle.api.tasks.InputFiles;
|
||||||
import org.gradle.api.tasks.Internal;
|
|
||||||
import org.gradle.api.tasks.Optional;
|
import org.gradle.api.tasks.Optional;
|
||||||
import org.gradle.api.tasks.OutputDirectory;
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -193,7 +183,7 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
public void checkDependencies() throws IOException, NoSuchAlgorithmException {
|
public void checkDependencies() {
|
||||||
if (dependencies == null) {
|
if (dependencies == null) {
|
||||||
throw new GradleException("No dependencies variable defined.");
|
throw new GradleException("No dependencies variable defined.");
|
||||||
}
|
}
|
||||||
|
@ -214,12 +204,9 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
Map<String, Boolean> licenses = new HashMap<>();
|
Map<String, Boolean> licenses = new HashMap<>();
|
||||||
Map<String, Boolean> notices = new HashMap<>();
|
Map<String, Boolean> notices = new HashMap<>();
|
||||||
Map<String, Boolean> sources = new HashMap<>();
|
Map<String, Boolean> sources = new HashMap<>();
|
||||||
Set<File> shaFiles = new HashSet<>();
|
|
||||||
for (File file : licensesDirAsFile.listFiles()) {
|
for (File file : licensesDirAsFile.listFiles()) {
|
||||||
String name = file.getName();
|
String name = file.getName();
|
||||||
if (name.endsWith(SHA_EXTENSION)) {
|
if (name.endsWith("-LICENSE") || name.endsWith("-LICENSE.txt")) {
|
||||||
shaFiles.add(file);
|
|
||||||
} else if (name.endsWith("-LICENSE") || name.endsWith("-LICENSE.txt")) {
|
|
||||||
// TODO: why do we support suffix of LICENSE *and* LICENSE.txt??
|
// TODO: why do we support suffix of LICENSE *and* LICENSE.txt??
|
||||||
licenses.put(name, false);
|
licenses.put(name, false);
|
||||||
} else if (name.contains("-NOTICE") || name.contains("-NOTICE.txt")) {
|
} else if (name.contains("-NOTICE") || name.contains("-NOTICE.txt")) {
|
||||||
|
@ -233,18 +220,13 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
notices.keySet().removeAll(ignoreFiles);
|
notices.keySet().removeAll(ignoreFiles);
|
||||||
sources.keySet().removeAll(ignoreFiles);
|
sources.keySet().removeAll(ignoreFiles);
|
||||||
|
|
||||||
checkDependencies(licenses, notices, sources, shaFiles);
|
checkDependencies(licenses, notices, sources);
|
||||||
|
|
||||||
licenses.forEach((item, exists) -> failIfAnyMissing(item, exists, "license"));
|
licenses.forEach((item, exists) -> failIfAnyMissing(item, exists, "license"));
|
||||||
|
|
||||||
notices.forEach((item, exists) -> failIfAnyMissing(item, exists, "notice"));
|
notices.forEach((item, exists) -> failIfAnyMissing(item, exists, "notice"));
|
||||||
|
|
||||||
sources.forEach((item, exists) -> failIfAnyMissing(item, exists, "sources"));
|
sources.forEach((item, exists) -> failIfAnyMissing(item, exists, "sources"));
|
||||||
|
|
||||||
if (shaFiles.isEmpty() == false) {
|
|
||||||
throw new GradleException("Unused sha files found: \n" + joinFilenames(shaFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is just a marker output folder to allow this task being up-to-date.
|
// This is just a marker output folder to allow this task being up-to-date.
|
||||||
|
@ -261,18 +243,10 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDependencies(
|
private void checkDependencies(Map<String, Boolean> licenses, Map<String, Boolean> notices, Map<String, Boolean> sources) {
|
||||||
Map<String, Boolean> licenses,
|
|
||||||
Map<String, Boolean> notices,
|
|
||||||
Map<String, Boolean> sources,
|
|
||||||
Set<File> shaFiles
|
|
||||||
) throws NoSuchAlgorithmException, IOException {
|
|
||||||
for (File dependency : dependencies) {
|
for (File dependency : dependencies) {
|
||||||
String jarName = dependency.getName();
|
String jarName = dependency.getName();
|
||||||
String depName = regex.matcher(jarName).replaceFirst("");
|
String depName = regex.matcher(jarName).replaceFirst("");
|
||||||
|
|
||||||
validateSha(shaFiles, dependency, jarName, depName);
|
|
||||||
|
|
||||||
String dependencyName = getDependencyName(mappings, depName);
|
String dependencyName = getDependencyName(mappings, depName);
|
||||||
logger.info("mapped dependency name {} to {} for license/notice check", depName, dependencyName);
|
logger.info("mapped dependency name {} to {} for license/notice check", depName, dependencyName);
|
||||||
checkFile(dependencyName, jarName, licenses, "LICENSE");
|
checkFile(dependencyName, jarName, licenses, "LICENSE");
|
||||||
|
@ -286,24 +260,6 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateSha(Set<File> shaFiles, File dependency, String jarName, String depName) throws NoSuchAlgorithmException,
|
|
||||||
IOException {
|
|
||||||
if (ignoreShas.contains(depName)) {
|
|
||||||
// local deps should not have sha files!
|
|
||||||
if (getShaFile(jarName).exists()) {
|
|
||||||
throw new GradleException("SHA file " + getShaFile(jarName) + " exists for ignored dependency " + depName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.info("Checking sha for {}", jarName);
|
|
||||||
checkSha(dependency, jarName, shaFiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String joinFilenames(Set<File> shaFiles) {
|
|
||||||
List<String> names = shaFiles.stream().map(File::getName).collect(Collectors.toList());
|
|
||||||
return String.join("\n", names);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getDependencyName(Map<String, String> mappings, String dependencyName) {
|
public static String getDependencyName(Map<String, String> mappings, String dependencyName) {
|
||||||
// order is the same for keys and values iteration since we use a linked hashmap
|
// order is the same for keys and values iteration since we use a linked hashmap
|
||||||
List<String> mapped = new ArrayList<>(mappings.values());
|
List<String> mapped = new ArrayList<>(mappings.values());
|
||||||
|
@ -319,30 +275,6 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
return dependencyName;
|
return dependencyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSha(File jar, String jarName, Set<File> shaFiles) throws NoSuchAlgorithmException, IOException {
|
|
||||||
File shaFile = getShaFile(jarName);
|
|
||||||
if (shaFile.exists() == false) {
|
|
||||||
throw new GradleException("Missing SHA for " + jarName + ". Run \"gradle updateSHAs\" to create them");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: shouldn't have to trim, sha files should not have trailing newline
|
|
||||||
byte[] fileBytes = Files.readAllBytes(shaFile.toPath());
|
|
||||||
String expectedSha = new String(fileBytes, StandardCharsets.UTF_8).trim();
|
|
||||||
|
|
||||||
String sha = getSha1(jar);
|
|
||||||
|
|
||||||
if (expectedSha.equals(sha) == false) {
|
|
||||||
final String exceptionMessage = String.format(Locale.ROOT, """
|
|
||||||
SHA has changed! Expected %s for %s but got %s.
|
|
||||||
This usually indicates a corrupt dependency cache or artifacts changed upstream.
|
|
||||||
Either wipe your cache, fix the upstream artifact, or delete %s and run updateShas
|
|
||||||
""", expectedSha, jarName, sha, shaFile);
|
|
||||||
|
|
||||||
throw new GradleException(exceptionMessage);
|
|
||||||
}
|
|
||||||
shaFiles.remove(shaFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFile(String name, String jarName, Map<String, Boolean> counters, String type) {
|
private void checkFile(String name, String jarName, Map<String, Boolean> counters, String type) {
|
||||||
String fileName = getFileName(name, counters, type);
|
String fileName = getFileName(name, counters, type);
|
||||||
|
|
||||||
|
@ -375,27 +307,4 @@ public class DependencyLicensesTask extends DefaultTask {
|
||||||
return new LinkedHashMap<>(mappings);
|
return new LinkedHashMap<>(mappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
File getShaFile(String jarName) {
|
|
||||||
return new File(licensesDir.get().getAsFile(), jarName + SHA_EXTENSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
Set<File> getShaFiles() {
|
|
||||||
File licenseDirAsFile = licensesDir.get().getAsFile();
|
|
||||||
File[] array = licenseDirAsFile.listFiles();
|
|
||||||
if (array == null) {
|
|
||||||
throw new GradleException("\"" + licenseDirAsFile.getPath() + "\" isn't a valid directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arrays.stream(array).filter(file -> file.getName().endsWith(SHA_EXTENSION)).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
String getSha1(File file) throws IOException, NoSuchAlgorithmException {
|
|
||||||
byte[] bytes = Files.readAllBytes(file.toPath());
|
|
||||||
|
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
|
||||||
char[] encoded = Hex.encodeHex(digest.digest(bytes));
|
|
||||||
return String.copyValueOf(encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
||||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
||||||
* Side Public License, v 1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.gradle.internal.precommit;
|
|
||||||
|
|
||||||
import org.gradle.api.DefaultTask;
|
|
||||||
import org.gradle.api.logging.Logger;
|
|
||||||
import org.gradle.api.logging.Logging;
|
|
||||||
import org.gradle.api.tasks.Internal;
|
|
||||||
import org.gradle.api.tasks.TaskAction;
|
|
||||||
import org.gradle.api.tasks.TaskProvider;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A task to update shas used by {@code DependencyLicensesCheck}
|
|
||||||
*/
|
|
||||||
public class UpdateShasTask extends DefaultTask {
|
|
||||||
|
|
||||||
private final Logger logger = Logging.getLogger(getClass());
|
|
||||||
|
|
||||||
/** The parent dependency licenses task to use configuration from */
|
|
||||||
private TaskProvider<DependencyLicensesTask> parentTask;
|
|
||||||
|
|
||||||
public UpdateShasTask() {
|
|
||||||
setDescription("Updates the sha files for the dependencyLicenses check");
|
|
||||||
setOnlyIf(element -> parentTask.get().getLicensesDir() != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
public void updateShas() throws NoSuchAlgorithmException, IOException {
|
|
||||||
Set<File> shaFiles = parentTask.get().getShaFiles();
|
|
||||||
|
|
||||||
for (File dependency : parentTask.get().getDependencies()) {
|
|
||||||
String jarName = dependency.getName();
|
|
||||||
File shaFile = parentTask.get().getShaFile(jarName);
|
|
||||||
|
|
||||||
if (shaFile.exists() == false) {
|
|
||||||
createSha(dependency, jarName, shaFile);
|
|
||||||
} else {
|
|
||||||
shaFiles.remove(shaFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (File shaFile : shaFiles) {
|
|
||||||
logger.lifecycle("Removing unused sha " + shaFile.getName());
|
|
||||||
shaFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createSha(File dependency, String jarName, File shaFile) throws IOException, NoSuchAlgorithmException {
|
|
||||||
logger.lifecycle("Adding sha for " + jarName);
|
|
||||||
|
|
||||||
String sha = parentTask.get().getSha1(dependency);
|
|
||||||
|
|
||||||
Files.write(shaFile.toPath(), sha.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
public DependencyLicensesTask getParentTask() {
|
|
||||||
return parentTask.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParentTask(TaskProvider<DependencyLicensesTask> parentTask) {
|
|
||||||
this.parentTask = parentTask;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,8 +26,6 @@ import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -41,8 +39,6 @@ public class DependencyLicensesTaskTests {
|
||||||
@Rule
|
@Rule
|
||||||
public ExpectedException expectedException = ExpectedException.none();
|
public ExpectedException expectedException = ExpectedException.none();
|
||||||
|
|
||||||
private UpdateShasTask updateShas;
|
|
||||||
|
|
||||||
private TaskProvider<DependencyLicensesTask> task;
|
private TaskProvider<DependencyLicensesTask> task;
|
||||||
|
|
||||||
private Project project;
|
private Project project;
|
||||||
|
@ -53,7 +49,6 @@ public class DependencyLicensesTaskTests {
|
||||||
public void prepare() {
|
public void prepare() {
|
||||||
project = createProject();
|
project = createProject();
|
||||||
task = createDependencyLicensesTask(project);
|
task = createDependencyLicensesTask(project);
|
||||||
updateShas = createUpdateShasTask(project, task);
|
|
||||||
dependency = project.getDependencies().localGroovy();
|
dependency = project.getDependencies().localGroovy();
|
||||||
task.configure(new Action<DependencyLicensesTask>() {
|
task.configure(new Action<DependencyLicensesTask>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,19 +82,6 @@ public class DependencyLicensesTaskTests {
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenProjectWithDependencyButNoShaFileThenShouldReturnException() throws Exception {
|
|
||||||
expectedException.expect(GradleException.class);
|
|
||||||
expectedException.expectMessage(containsString("Missing SHA for "));
|
|
||||||
|
|
||||||
File licensesDir = getLicensesDir(project);
|
|
||||||
createFileIn(licensesDir, "groovy-all-LICENSE.txt", PERMISSIVE_LICENSE_TEXT);
|
|
||||||
createFileIn(licensesDir, "groovy-all-NOTICE.txt", "");
|
|
||||||
|
|
||||||
project.getDependencies().add("implementation", project.getDependencies().localGroovy());
|
|
||||||
task.get().checkDependencies();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenProjectWithDependencyButNoLicenseFileThenShouldReturnException() throws Exception {
|
public void givenProjectWithDependencyButNoLicenseFileThenShouldReturnException() throws Exception {
|
||||||
expectedException.expect(GradleException.class);
|
expectedException.expect(GradleException.class);
|
||||||
|
@ -108,7 +90,6 @@ public class DependencyLicensesTaskTests {
|
||||||
project.getDependencies().add("implementation", project.getDependencies().localGroovy());
|
project.getDependencies().add("implementation", project.getDependencies().localGroovy());
|
||||||
|
|
||||||
getLicensesDir(project).mkdir();
|
getLicensesDir(project).mkdir();
|
||||||
updateShas.updateShas();
|
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +102,6 @@ public class DependencyLicensesTaskTests {
|
||||||
|
|
||||||
createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", PERMISSIVE_LICENSE_TEXT);
|
createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", PERMISSIVE_LICENSE_TEXT);
|
||||||
|
|
||||||
updateShas.updateShas();
|
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +115,6 @@ public class DependencyLicensesTaskTests {
|
||||||
createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", STRICT_LICENSE_TEXT);
|
createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", STRICT_LICENSE_TEXT);
|
||||||
createFileIn(getLicensesDir(project), "groovy-NOTICE.txt", "");
|
createFileIn(getLicensesDir(project), "groovy-NOTICE.txt", "");
|
||||||
|
|
||||||
updateShas.updateShas();
|
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +126,6 @@ public class DependencyLicensesTaskTests {
|
||||||
createFileIn(getLicensesDir(project), "groovy-NOTICE.txt", "");
|
createFileIn(getLicensesDir(project), "groovy-NOTICE.txt", "");
|
||||||
createFileIn(getLicensesDir(project), "groovy-SOURCES.txt", "");
|
createFileIn(getLicensesDir(project), "groovy-SOURCES.txt", "");
|
||||||
|
|
||||||
updateShas.updateShas();
|
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,37 +168,6 @@ public class DependencyLicensesTaskTests {
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenProjectWithAShaButWithoutTheDependencyThenShouldThrowException() throws Exception {
|
|
||||||
expectedException.expect(GradleException.class);
|
|
||||||
expectedException.expectMessage(containsString("Unused sha files found: \n"));
|
|
||||||
|
|
||||||
project.getDependencies().add("implementation", dependency);
|
|
||||||
|
|
||||||
File licensesDir = getLicensesDir(project);
|
|
||||||
createAllDefaultDependencyFiles(licensesDir, "groovy");
|
|
||||||
createFileIn(licensesDir, "non-declared.sha1", "");
|
|
||||||
|
|
||||||
task.get().checkDependencies();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenProjectWithADependencyWithWrongShaThenShouldThrowException() throws Exception {
|
|
||||||
expectedException.expect(GradleException.class);
|
|
||||||
expectedException.expectMessage(containsString("SHA has changed! Expected "));
|
|
||||||
|
|
||||||
project.getDependencies().add("implementation", dependency);
|
|
||||||
|
|
||||||
File licensesDir = getLicensesDir(project);
|
|
||||||
createAllDefaultDependencyFiles(licensesDir, "groovy");
|
|
||||||
|
|
||||||
Path groovySha = Files.list(licensesDir.toPath()).filter(file -> file.toFile().getName().contains("sha")).findFirst().get();
|
|
||||||
|
|
||||||
Files.write(groovySha, new byte[] { 1 }, StandardOpenOption.CREATE);
|
|
||||||
|
|
||||||
task.get().checkDependencies();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenProjectWithADependencyMappingThenShouldReturnSilently() throws Exception {
|
public void givenProjectWithADependencyMappingThenShouldReturnSilently() throws Exception {
|
||||||
project.getDependencies().add("implementation", dependency);
|
project.getDependencies().add("implementation", dependency);
|
||||||
|
@ -261,14 +208,6 @@ public class DependencyLicensesTaskTests {
|
||||||
task.get().checkDependencies();
|
task.get().checkDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenProjectWithoutLicensesDirWhenAskingForShaFilesThenShouldThrowException() {
|
|
||||||
expectedException.expect(GradleException.class);
|
|
||||||
expectedException.expectMessage(containsString("isn't a valid directory"));
|
|
||||||
|
|
||||||
task.get().getShaFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Project createProject() {
|
private Project createProject() {
|
||||||
Project project = ProjectBuilder.builder().build();
|
Project project = ProjectBuilder.builder().build();
|
||||||
project.getPlugins().apply(JavaPlugin.class);
|
project.getPlugins().apply(JavaPlugin.class);
|
||||||
|
@ -276,11 +215,9 @@ public class DependencyLicensesTaskTests {
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAllDefaultDependencyFiles(File licensesDir, String dependencyName) throws IOException, NoSuchAlgorithmException {
|
private void createAllDefaultDependencyFiles(File licensesDir, String dependencyName) throws IOException {
|
||||||
createFileIn(licensesDir, dependencyName + "-LICENSE.txt", PERMISSIVE_LICENSE_TEXT);
|
createFileIn(licensesDir, dependencyName + "-LICENSE.txt", PERMISSIVE_LICENSE_TEXT);
|
||||||
createFileIn(licensesDir, dependencyName + "-NOTICE.txt", "");
|
createFileIn(licensesDir, dependencyName + "-NOTICE.txt", "");
|
||||||
|
|
||||||
updateShas.updateShas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getLicensesDir(Project project) {
|
private File getLicensesDir(Project project) {
|
||||||
|
@ -300,13 +237,6 @@ public class DependencyLicensesTaskTests {
|
||||||
Files.write(file, content.getBytes(StandardCharsets.UTF_8));
|
Files.write(file, content.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
private UpdateShasTask createUpdateShasTask(Project project, TaskProvider<DependencyLicensesTask> dependencyLicensesTask) {
|
|
||||||
UpdateShasTask task = project.getTasks().register("updateShas", UpdateShasTask.class).get();
|
|
||||||
|
|
||||||
task.setParentTask(dependencyLicensesTask);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TaskProvider<DependencyLicensesTask> createDependencyLicensesTask(Project project) {
|
private TaskProvider<DependencyLicensesTask> createDependencyLicensesTask(Project project) {
|
||||||
TaskProvider<DependencyLicensesTask> task = project.getTasks()
|
TaskProvider<DependencyLicensesTask> task = project.getTasks()
|
||||||
.register("dependencyLicenses", DependencyLicensesTask.class, new Action<DependencyLicensesTask>() {
|
.register("dependencyLicenses", DependencyLicensesTask.class, new Action<DependencyLicensesTask>() {
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
||||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
||||||
* Side Public License, v 1.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.gradle.internal.precommit;
|
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.gradle.api.GradleException;
|
|
||||||
import org.gradle.api.Project;
|
|
||||||
import org.gradle.api.artifacts.Dependency;
|
|
||||||
import org.gradle.api.file.FileCollection;
|
|
||||||
import org.gradle.api.plugins.JavaPlugin;
|
|
||||||
import org.gradle.api.tasks.TaskProvider;
|
|
||||||
import org.gradle.testfixtures.ProjectBuilder;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.ExpectedException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class UpdateShasTaskTests {
|
|
||||||
|
|
||||||
public static final String GROOVY_JAR_REGEX = "groovy-\\d\\.\\d+\\.\\d+\\.jar";
|
|
||||||
@Rule
|
|
||||||
public ExpectedException expectedException = ExpectedException.none();
|
|
||||||
|
|
||||||
private UpdateShasTask task;
|
|
||||||
|
|
||||||
private Project project;
|
|
||||||
|
|
||||||
private Dependency dependency;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void prepare() throws IOException {
|
|
||||||
project = createProject();
|
|
||||||
task = createUpdateShasTask(project);
|
|
||||||
dependency = project.getDependencies().localGroovy();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenDependencyDoesntExistThenShouldDeleteDependencySha() throws IOException, NoSuchAlgorithmException {
|
|
||||||
File unusedSha = createFileIn(getLicensesDir(project), "test.sha1", "");
|
|
||||||
task.updateShas();
|
|
||||||
|
|
||||||
assertFalse(unusedSha.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenDependencyExistsButShaNotThenShouldCreateNewShaFile() throws IOException, NoSuchAlgorithmException {
|
|
||||||
project.getDependencies().add("implementation", dependency);
|
|
||||||
|
|
||||||
getLicensesDir(project).mkdir();
|
|
||||||
task.updateShas();
|
|
||||||
Path groovySha = Files.list(getLicensesDir(project).toPath())
|
|
||||||
.filter(p -> p.toFile().getName().matches(GROOVY_JAR_REGEX + ".sha1"))
|
|
||||||
.findFirst()
|
|
||||||
.get();
|
|
||||||
assertTrue(groovySha.toFile().getName().startsWith("groovy"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenDependencyAndWrongShaExistsThenShouldNotOverwriteShaFile() throws IOException, NoSuchAlgorithmException {
|
|
||||||
project.getDependencies().add("implementation", dependency);
|
|
||||||
File groovyJar = task.getParentTask()
|
|
||||||
.getDependencies()
|
|
||||||
.getFiles()
|
|
||||||
.stream()
|
|
||||||
.filter(f -> f.getName().matches(GROOVY_JAR_REGEX))
|
|
||||||
.findFirst()
|
|
||||||
.get();
|
|
||||||
String groovyShaName = groovyJar.getName() + ".sha1";
|
|
||||||
File groovySha = createFileIn(getLicensesDir(project), groovyShaName, "content");
|
|
||||||
task.updateShas();
|
|
||||||
assertThat(FileUtils.readFileToString(groovySha), equalTo("content"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenLicensesDirDoesntExistThenShouldThrowException() throws IOException, NoSuchAlgorithmException {
|
|
||||||
expectedException.expect(GradleException.class);
|
|
||||||
expectedException.expectMessage(containsString("isn't a valid directory"));
|
|
||||||
|
|
||||||
task.updateShas();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Project createProject() {
|
|
||||||
Project project = ProjectBuilder.builder().build();
|
|
||||||
project.getPlugins().apply(JavaPlugin.class);
|
|
||||||
|
|
||||||
return project;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getLicensesDir(Project project) {
|
|
||||||
return getFile(project, "licenses");
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getFile(Project project, String fileName) {
|
|
||||||
return project.getProjectDir().toPath().resolve(fileName).toFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createFileIn(File parent, String name, String content) throws IOException {
|
|
||||||
parent.mkdir();
|
|
||||||
|
|
||||||
Path path = parent.toPath().resolve(name);
|
|
||||||
File file = path.toFile();
|
|
||||||
|
|
||||||
Files.write(path, content.getBytes(), StandardOpenOption.CREATE);
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UpdateShasTask createUpdateShasTask(Project project) {
|
|
||||||
UpdateShasTask task = project.getTasks().register("updateShas", UpdateShasTask.class).get();
|
|
||||||
|
|
||||||
task.setParentTask(createDependencyLicensesTask(project));
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TaskProvider<DependencyLicensesTask> createDependencyLicensesTask(Project project) {
|
|
||||||
return project.getTasks()
|
|
||||||
.register(
|
|
||||||
"dependencyLicenses",
|
|
||||||
DependencyLicensesTask.class,
|
|
||||||
dependencyLicensesTask -> dependencyLicensesTask.setDependencies(getDependencies(project))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileCollection getDependencies(Project project) {
|
|
||||||
return project.getConfigurations().getByName("compileClasspath");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
3cb1181b2141a7e752f5bdc998b7ef1849f726cf
|
|
|
@ -1 +0,0 @@
|
||||||
f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f
|
|
|
@ -1 +0,0 @@
|
||||||
cd18227f1eb8e9a263286c1d7362ceb24f6f9b32
|
|
|
@ -1 +0,0 @@
|
||||||
e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada
|
|
|
@ -1 +0,0 @@
|
||||||
853b96d3afbb7bf8cc303fe27ee96836a10c1834
|
|
|
@ -1 +0,0 @@
|
||||||
3f897ace4d7f10f0ea6a58f524a3b105dd483653
|
|
|
@ -1 +0,0 @@
|
||||||
3cb1181b2141a7e752f5bdc998b7ef1849f726cf
|
|
|
@ -1 +0,0 @@
|
||||||
f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f
|
|
|
@ -1 +0,0 @@
|
||||||
e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada
|
|
|
@ -1 +0,0 @@
|
||||||
853b96d3afbb7bf8cc303fe27ee96836a10c1834
|
|
|
@ -1 +0,0 @@
|
||||||
0a6a0e0620d51833feffc67bccb51937b2345763
|
|
|
@ -1 +0,0 @@
|
||||||
321c614f85f1dea6bb08c1817c60d53b7f3552fd
|
|
|
@ -1 +0,0 @@
|
||||||
4fb5db5f03d00f6a94e43b78d097978190e4abb2
|
|
|
@ -1 +0,0 @@
|
||||||
1a838a87959d9c2cee658f4a4e1869e28f6b9976
|
|
|
@ -17,3 +17,6 @@ systemProp.jdk.tls.client.protocols=TLSv1.2
|
||||||
# java homes resolved by environment variables
|
# java homes resolved by environment variables
|
||||||
org.gradle.java.installations.auto-detect=false
|
org.gradle.java.installations.auto-detect=false
|
||||||
org.gradle.java.installations.fromEnv=JAVA_TOOLCHAIN_HOME,JAVA_HOME,RUNTIME_JAVA_HOME,JAVA19_HOME,JAVA18_HOME,JAVA17_HOME,JAVA16_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME
|
org.gradle.java.installations.fromEnv=JAVA_TOOLCHAIN_HOME,JAVA_HOME,RUNTIME_JAVA_HOME,JAVA19_HOME,JAVA18_HOME,JAVA17_HOME,JAVA16_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME
|
||||||
|
|
||||||
|
# log some dependency verification info to console
|
||||||
|
org.gradle.dependency.verification.console=verbose
|
|
@ -25,10 +25,10 @@ json-assert = "org.skyscreamer:jsonassert:1.5.0"
|
||||||
jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name="jackson-dataformat-yaml", version.ref="jackson" }
|
jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name="jackson-dataformat-yaml", version.ref="jackson" }
|
||||||
jackson-platform = { group = "com.fasterxml.jackson", name="jackson-bom", version.ref="jackson" }
|
jackson-platform = { group = "com.fasterxml.jackson", name="jackson-bom", version.ref="jackson" }
|
||||||
jna = "net.java.dev.jna:jna:5.10.0"
|
jna = "net.java.dev.jna:jna:5.10.0"
|
||||||
junit = "junit:junit:4.12"
|
junit = "junit:junit:4.13.2"
|
||||||
junit5-platform = { group = "org.junit", name="junit-bom", version.ref="junit5" }
|
junit5-platform = { group = "org.junit", name="junit-bom", version.ref="junit5" }
|
||||||
junit5-jupiter = { group = "org.junit.jupiter", name="junit-jupiter", version.ref="junit5" }
|
junit5-jupiter = { group = "org.junit.jupiter", name="junit-jupiter", version.ref="junit5" }
|
||||||
junit5-platform-launcher = "org.junit.platform:junit-platform-launcher:1.8.0"
|
junit5-platform-launcher = "org.junit.platform:junit-platform-launcher:1.8.1"
|
||||||
junit5-vintage = { group = "org.junit.vintage", name="junit-vintage-engine", version.ref="junit5" }
|
junit5-vintage = { group = "org.junit.vintage", name="junit-vintage-engine", version.ref="junit5" }
|
||||||
maven-model = "org.apache.maven:maven-model:3.6.2"
|
maven-model = "org.apache.maven:maven-model:3.6.2"
|
||||||
mockito-core = "org.mockito:mockito-core:1.9.5"
|
mockito-core = "org.mockito:mockito-core:1.9.5"
|
||||||
|
|
3600
gradle/verification-metadata.xml
Normal file
3600
gradle/verification-metadata.xml
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
||||||
98cafc6081d5632b61be2c9e60650b64ddbc637c
|
|
|
@ -1 +0,0 @@
|
||||||
a6884b2fd8fd9a56874db05afaa22435043a2e3e
|
|
|
@ -1 +0,0 @@
|
||||||
3cb751702e1194ff24259155db4d37e9383d4320
|
|
|
@ -1 +0,0 @@
|
||||||
4b986a99445e49ea5fbf5d149c4b63f6ed6c6780
|
|
|
@ -1 +0,0 @@
|
||||||
0a6a0e0620d51833feffc67bccb51937b2345763
|
|
|
@ -1 +0,0 @@
|
||||||
4fc77e1ec6922fc48bf1181e4b38f600dac222ff
|
|
|
@ -1 +0,0 @@
|
||||||
984bb22f310ebbedc967d206e672f8acf766a98e
|
|
|
@ -1 +0,0 @@
|
||||||
5601496b5b6e43d947aeeffbffadb2b18961c731
|
|
|
@ -1 +0,0 @@
|
||||||
8fde7fe2586328ac3c68db92045e1c8759125000
|
|
|
@ -1 +0,0 @@
|
||||||
8467c813d442837fcaeddbc42cf5c5359fab4933
|
|
|
@ -1 +0,0 @@
|
||||||
0fc7258f948358c8caace27b9b191437a50a7ecc
|
|
|
@ -1 +0,0 @@
|
||||||
6808f50c447fb033b334ca5ca25830647d85abe1
|
|
|
@ -1 +0,0 @@
|
||||||
3cb1181b2141a7e752f5bdc998b7ef1849f726cf
|
|
|
@ -1 +0,0 @@
|
||||||
a4cf4688fe1c7e3a63aa636cc96d013af537768e
|
|
|
@ -1 +0,0 @@
|
||||||
4ec95b60d4e86b5c95a0e919cb172a0af98011ef
|
|
|
@ -1 +0,0 @@
|
||||||
a2503f302b11ebde7ebc3df41daebe0e4eea3689
|
|
|
@ -1 +0,0 @@
|
||||||
0122c7cee69b53ed4a7681c03d4ee4c0e2765da5
|
|
|
@ -1 +0,0 @@
|
||||||
f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f
|
|
|
@ -1 +0,0 @@
|
||||||
e4ba98f1d4b3c80ec46392f25e094a6a2e58fcbf
|
|
|
@ -1 +0,0 @@
|
||||||
4e416198adde54b753e41d3312f799640dac5687
|
|
|
@ -1 +0,0 @@
|
||||||
1f41de81768ef84ca2d8cda4cb79e9272c8ee966
|
|
|
@ -1 +0,0 @@
|
||||||
cd49678784c46aa8789c060538e0154013bb421b
|
|
|
@ -1 +0,0 @@
|
||||||
67b85a6aea4a1c846448e3513161d6c260d6e0d9
|
|
|
@ -1 +0,0 @@
|
||||||
5513d31545085c33809c4b6553c2009fd19a6016
|
|
|
@ -1 +0,0 @@
|
||||||
a201b5bdc92c0fae4bed4b8e5546388c4c2f9eb0
|
|
|
@ -1 +0,0 @@
|
||||||
5df31b69375131fc2163a5557093cb112be90ce1
|
|
|
@ -1 +0,0 @@
|
||||||
8c5cd5f1b3e7b3656ab983b73bbbf8bf5f14f793
|
|
|
@ -1 +0,0 @@
|
||||||
8619e95939167fb37245b5670135e4feb0ec7d50
|
|
|
@ -1 +0,0 @@
|
||||||
5584627487e984c03456266d3f8802eb85a9ce97
|
|
|
@ -1 +0,0 @@
|
||||||
97b2454943127857a8304319be658d6d7ff4fff1
|
|
|
@ -1 +0,0 @@
|
||||||
f36cad41d61ad3a49e61ca6a2327cf364ec110e1
|
|
|
@ -1 +0,0 @@
|
||||||
6d3fcb9c539fde6ba7f175a82fa14466e69ba7a2
|
|
|
@ -1 +0,0 @@
|
||||||
02a220afa62cc703233fe9b1787128e4391f59c5
|
|
|
@ -1 +0,0 @@
|
||||||
433b17482c209554449abc5503e65b9e1feeefbc
|
|
|
@ -1 +0,0 @@
|
||||||
7ada9deb2ef1cd17c1b4313147dac5aa4b965e6d
|
|
|
@ -1 +0,0 @@
|
||||||
0fda489271d30f6f0e3f7f92908d029f1b76c5e2
|
|
|
@ -1 +0,0 @@
|
||||||
6b18b232d4ae95ab67839f46f3e8413e7cc12eab
|
|
|
@ -1 +0,0 @@
|
||||||
181647dc6b748be73410f8a624a8b279f2e75407
|
|
|
@ -1 +0,0 @@
|
||||||
6fe6806d2604441f770ad775f6cba2fc6e1032b9
|
|
|
@ -1 +0,0 @@
|
||||||
a35f88b193c8e43bdf909e022a0325b306a43c87
|
|
|
@ -1 +0,0 @@
|
||||||
e1ef1382ae9dfb2438b82b6dd575566355c2f30f
|
|
|
@ -1 +0,0 @@
|
||||||
c4f7d054303948eb6a4066194253886c8af07128
|
|
|
@ -1 +0,0 @@
|
||||||
e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada
|
|
|
@ -1 +0,0 @@
|
||||||
853b96d3afbb7bf8cc303fe27ee96836a10c1834
|
|
|
@ -1 +0,0 @@
|
||||||
523b0d63f3dbeacb7dfceb7431cb17fa56cf79fa
|
|
|
@ -1 +0,0 @@
|
||||||
1cbcbe4623113e6af92ccaa89884a345270f1a87
|
|
|
@ -1 +0,0 @@
|
||||||
51ae921a2ed1e06ca8876f12f32f265e83c0b2b8
|
|
|
@ -1 +0,0 @@
|
||||||
698b2d2b15d9a1b7aae025f1d9f576842285e7f6
|
|
|
@ -1 +0,0 @@
|
||||||
e7e0fd82da0a160b7928ba214e699a7e6a74fff4
|
|
|
@ -1 +0,0 @@
|
||||||
66144204f9d6d7d3f3f775622c2dd7e9bd511d97
|
|
|
@ -1 +0,0 @@
|
||||||
fa637eb67eb7628c915d73762b681ae7ff0b9731
|
|
|
@ -1 +0,0 @@
|
||||||
b6e6abe057f23630113f4167c34bda7086691258
|
|
|
@ -1 +0,0 @@
|
||||||
ca2954e8d92a05bacc28ff465b25c70e0f512497
|
|
|
@ -1 +0,0 @@
|
||||||
3a23cc36edaf8fc5a89cb100182758ccb5991487
|
|
|
@ -1 +0,0 @@
|
||||||
6111ae24e3be9ecbd75f5fe908583fc14b4f0174
|
|
|
@ -1 +0,0 @@
|
||||||
2609e36f18f7e8d593cc1cddfb2ac776dc96b8e0
|
|
|
@ -1 +0,0 @@
|
||||||
fa637eb67eb7628c915d73762b681ae7ff0b9731
|
|
|
@ -1 +0,0 @@
|
||||||
b6e6abe057f23630113f4167c34bda7086691258
|
|
|
@ -1 +0,0 @@
|
||||||
ca2954e8d92a05bacc28ff465b25c70e0f512497
|
|
|
@ -1 +0,0 @@
|
||||||
3a23cc36edaf8fc5a89cb100182758ccb5991487
|
|
|
@ -1 +0,0 @@
|
||||||
a3ae34e57fa8a4040e28247291d0cc3d6b8c7bcf
|
|
|
@ -1 +0,0 @@
|
||||||
0a6a0e0620d51833feffc67bccb51937b2345763
|
|
|
@ -1 +0,0 @@
|
||||||
705981b7e25d05a76a3654e597dab6ba423eb79e
|
|
|
@ -1 +0,0 @@
|
||||||
84d3b2d97dd176bd705e4968a88fba0ea30fe991
|
|
|
@ -1 +0,0 @@
|
||||||
faa8ba85d503da4ab872d17ba8c00da0098ab2f2
|
|
|
@ -1 +0,0 @@
|
||||||
75a2db538d218e2bd3c2cbdf04c955b8f6db6626
|
|
|
@ -1 +0,0 @@
|
||||||
1d1f34b3e60db038f3913007a2706a820383dc26
|
|
|
@ -1 +0,0 @@
|
||||||
74b92065815f7affb0cd7897b683369b9ed982fd
|
|
|
@ -1 +0,0 @@
|
||||||
34f9c0563714e666ee6f44430152f46a5f760932
|
|
|
@ -1 +0,0 @@
|
||||||
ec18851f1976d5b810ae1a5fcc32520d2d38f77a
|
|
|
@ -1 +0,0 @@
|
||||||
0a6a0e0620d51833feffc67bccb51937b2345763
|
|
|
@ -1 +0,0 @@
|
||||||
ffeb635597d093509f33e1e94274d14be610f933
|
|
|
@ -1 +0,0 @@
|
||||||
cb6a722f128ff0ce2494384d419b6ff20fad25ab
|
|
|
@ -1 +0,0 @@
|
||||||
cddd9380efd4b81ea01e98be8fbdc9765a81793b
|
|
|
@ -1 +0,0 @@
|
||||||
e2f198c512f0f0ccbd6d618baecc9dde9975eadf
|
|
|
@ -1 +0,0 @@
|
||||||
562a587face36ec7eff2db7f2fc95425c6602bc1
|
|
|
@ -1 +0,0 @@
|
||||||
8d49996a4338670764d7ca4b85a1c4ccf7fe665d
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue