CheckRepositoryStructure.java 7.53 KB
Newer Older
package fr.emse.gitlab.saref.jobs;
Maxime Lefrançois's avatar
Maxime Lefrançois committed

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
import java.nio.file.FileSystems;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
import java.nio.file.Files;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
import java.nio.file.PathMatcher;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Maxime Lefrançois's avatar
Maxime Lefrançois committed
import fr.emse.gitlab.saref.Constants;
import fr.emse.gitlab.saref.SAREFPipelineException;
import fr.emse.gitlab.saref.entities.git.Version;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
public class CheckRepositoryStructure extends JobRunner {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	public static final PathMatcher ttlMatcher = FileSystems.getDefault().getPathMatcher("glob:**/*.ttl");

Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private final Set<String> gitignoreLines = new HashSet<>(
			Arrays.asList("target", "*~", ".DS_Store", "catalog-v001.xml", "saref-pipeline.jar"));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private Version version;
	private File directory;

	public CheckRepositoryStructure(String testSuiteName) {
		super(testSuiteName);
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	public void check(Version version) throws SAREFPipelineException {
		this.version = version;
		this.directory = version.getRepository().getDirectory();
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		checkThereExistsDirectory("diagrams");
		checkThereExistsDirectory("ontology");
		String repoName = checkRepoName();
		if (repoName == null) {
			return;
		}
		String ontologyFile = repoName.equals("saref-core") ? "saref.ttl" : repoName + ".ttl";
		checkOntoloyFolder(ontologyFile);
		checkGitIgnoreFolder();
		checkLICENSE();
		checkExamplesFolder();
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private void checkThereExistsDirectory(String dirName) throws SAREFPipelineException {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		final File dir = new File(directory, dirName);
		try {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			if (!dir.isDirectory()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				logger.warn(String.format("There should exist a folder named `%s`", dirName));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			}
		} catch (SecurityException ex) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error(String.format("Error while checking if there exists a folder named `%s`", dirName), ex);
			throw new SAREFPipelineException(String.format("Error while checking if there exists a folder named `%s`", dirName), ex);
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private String checkRepoName() throws SAREFPipelineException {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		String REGEX = "(?<ext>saref-core|saref4[a-z][a-z][a-z][a-z])";
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		String name = version.getRepositoryName();
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		Pattern pattern = Pattern.compile(REGEX);
		Matcher matcher = pattern.matcher(name);
		if (!matcher.find()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error(String.format(
Maxime Lefrançois's avatar
Maxime Lefrançois committed
					"The repository directory should contain `saref-core`, or `saref4abcd` (where abcd can be any sequence of four letters). Got: %s",
Maxime Lefrançois's avatar
Maxime Lefrançois committed
					name));
			throw new SAREFPipelineException(String.format(
Maxime Lefrançois's avatar
Maxime Lefrançois committed
					"The repository directory should contain `saref-core`, or `saref4abcd` (where abcd can be any sequence of four letters). Got: %s",
Maxime Lefrançois's avatar
Maxime Lefrançois committed
					name));
		}
		return matcher.group("ext");
	}

Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private void checkOntoloyFolder(String ontoName) throws SAREFPipelineException {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		final File ontoDir = new File(directory, "ontology");
		final File ontoFile = new File(ontoDir, ontoName);
		if (!ontoFile.isFile()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error(String.format("The ontology folder must contain an ontology file named `%s`", ontoName));
			throw new SAREFPipelineException(String.format("The ontology folder must contain an ontology file named `%s`", ontoName));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private void checkGitIgnoreFolder() throws SAREFPipelineException {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		final File gitignore = new File(directory, ".gitignore");
		if (!gitignore.exists()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.warn("The directory must contain a file named `.gitignore`");
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			return;
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		}
		try (BufferedReader br = Files.newBufferedReader(gitignore.toPath())) {
			for (String line = br.readLine(); line != null; line = br.readLine()) {
				gitignoreLines.remove(line);
			}
			if (!gitignoreLines.isEmpty()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				logger.warn(String.format("The file `.gitignore` lacks one line for each of the following strings: %s",
Maxime Lefrançois's avatar
Maxime Lefrançois committed
						gitignoreLines.toString()));
			}
		} catch (IOException ex) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error("Error while checking if the file `.gitignore` contains the appropriate lines: " + gitignoreLines, ex);
			throw new SAREFPipelineException("Error while checking if the file `.gitignore` contains the appropriate lines: " + gitignoreLines, ex);
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private void checkLICENSE() throws SAREFPipelineException {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		final File licenseFile = new File(directory, "LICENSE");
		if (!licenseFile.exists()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.warn("The directory must contain a file named `LICENSE`");
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			return;
		}
		InputStream licenseModelFile = CheckRepositoryStructure.class.getClassLoader().getResourceAsStream("LICENSE_MODEL");
		try (BufferedReader licenseModelFileReader = new BufferedReader(new InputStreamReader(licenseModelFile));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				BufferedReader licenseFileReader = new BufferedReader(new FileReader(licenseFile));) {

			// first line
			String modelLine = licenseModelFileReader.readLine();
			String licenseLine = licenseFileReader.readLine();
			String regex = modelLine.replaceFirst("YYYY", "(?<year>\\\\d{4})");
			Pattern pattern = Pattern.compile(String.format("^%s$", regex));
			Matcher regexMatcher = pattern.matcher(licenseLine);
			if (!regexMatcher.find()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				logger.warn("First line of the `LICENSE` file must match the following string (see details below)",
Maxime Lefrançois's avatar
Maxime Lefrançois committed
						"First line of the `LICENSE` file must match the following string, where YYYY is a year. Got: "
								+ licenseLine,
						modelLine);
				return;
			}
			int year = Integer.parseInt(regexMatcher.group("year"));
			int currentYear = Calendar.getInstance().get(Calendar.YEAR);
			if (year < 2015 || year > currentYear) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				logger.warn(String.format("The year in the `LICENSE` should be between %s and %s", 2015, currentYear));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				return;
			}

			int i = 1;
			modelLine = licenseModelFileReader.readLine();
			licenseLine = licenseFileReader.readLine();
			while (modelLine != null && licenseLine != null) {
				i++;
				if (!modelLine.equals(licenseLine)) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
					logger.warn(String.format("Line %s in the `LICENSE` should be `%s`", i, modelLine));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
					return;
				}
				modelLine = licenseModelFileReader.readLine();
				licenseLine = licenseFileReader.readLine();
			}
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			if (modelLine == null && licenseLine != null) {
				logger.warn(String.format("The license contains too many lines. Need %s", i));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				return;
			}
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			if (modelLine != null && licenseLine == null) {
				logger.warn(String.format("The license is incomplete.", i));
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				return;
			}
		} catch (Exception ex) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error("Exception while creating the path for the LICENSE model", ex);
			throw new SAREFPipelineException("Exception while creating the path for the LICENSE model", ex);
Maxime Lefrançois's avatar
Maxime Lefrançois committed
	private void checkExamplesFolder() throws SAREFPipelineException {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		final File dir = new File(directory, "examples");
		try {
			if (!dir.isDirectory()) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				logger.warn("There should exist a folder named `examples`");
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				return;
			}
		} catch (SecurityException ex) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error("Error while checking if there exists a folder named `examples`", ex);
			throw new SAREFPipelineException("Error while checking if there exists a folder named `examples`", ex);
Maxime Lefrançois's avatar
Maxime Lefrançois committed
		}
		try {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			boolean containsExample = Files.walk(dir.toPath())
					.anyMatch(p -> {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				return ttlMatcher.matches(p);
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			});
			if (!containsExample) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
				logger.warn("The `examples` folder should contain at least one example file (some `.ttl` document)");
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			}
		} catch (IOException ex) {
Maxime Lefrançois's avatar
Maxime Lefrançois committed
			logger.error("Exception while browsing the `examples` folder", ex);
			throw new SAREFPipelineException("Exception while browsing the `examples` folder", ex);