package com.loopperfect.buckaroo.routines; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.loopperfect.buckaroo.*; import com.loopperfect.buckaroo.io.IO; import java.io.IOException; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.loopperfect.buckaroo.routines.Routines.*; public final class InstallExisting { private InstallExisting() { } private static String buckarooDeps(final Stream<RecipeIdentifier> versions) { Preconditions.checkNotNull(versions); final String list = versions .map(x -> " '" + x.organization + "-" + x.recipe + "//:" + x.recipe + "', \n") .collect(Collectors.joining("")); return "# Generated by Buckaroo, do not edit!\n" + "BUCKAROO_DEPS = [\n" + list + "]\n"; } private static IO<Optional<IOException>> generateBuckarooDeps( final String path, final ImmutableMap<RecipeIdentifier, SemanticVersion> versions) { Preconditions.checkNotNull(path); Preconditions.checkNotNull(versions); return IO.writeFile(path, buckarooDeps(versions.keySet().stream()), true); } private static String buckConfig(final String rootPath, final ImmutableMap<RecipeIdentifier, SemanticVersion> versions) { Preconditions.checkNotNull(versions); final String list = versions.entrySet() .stream() .map(x -> " " + x.getKey().organization + "-" + x.getKey().recipe + " = " + rootPath + "/" + x.getKey().organization + "/" + x.getKey().recipe + "/" + x.getValue() + "/") .collect(Collectors.joining("\n")); return "# Generated by Buckaroo, do not edit!\n" + "[repositories]\n" + list + "\n"; } private static IO<Optional<IOException>> generateBuckConfig( final String path, String rootPath, final ImmutableMap<RecipeIdentifier, SemanticVersion> versions) { Preconditions.checkNotNull(path); Preconditions.checkNotNull(rootPath); Preconditions.checkNotNull(versions); return IO.writeFile(path, buckConfig(rootPath, versions), true); } private static IO<String> recipePath(final String dependenciesDirectory, final RecipeVersionIdentifier recipe) { Preconditions.checkNotNull(dependenciesDirectory); Preconditions.checkNotNull(recipe); return IO.of(x -> x.fs().getPath(dependenciesDirectory, "/", recipe.project.toString(), "/", recipe.version.toString(), "/").toString()); } private static IO<Optional<IOException>> fetchDependency( final String dependenciesDirectory, final RecipeVersionIdentifier identifier, final RecipeVersion recipeVersion, final ImmutableMap<RecipeIdentifier, SemanticVersion> resolvedDependencies) { Preconditions.checkNotNull(dependenciesDirectory); Preconditions.checkNotNull(identifier); Preconditions.checkNotNull(recipeVersion); Preconditions.checkNotNull(resolvedDependencies); return recipePath(dependenciesDirectory, identifier) .flatMap(path -> Routines.fetchSource(path, recipeVersion.source)) .map(x -> x.map(IOException::new)); } private static ImmutableMap<RecipeIdentifier, SemanticVersion> refineDependencies( final Optional<RecipeIdentifier> identifier, final ImmutableMap<RecipeIdentifier, SemanticVersion> resolvedDependencies, final DependencyGroup dependencies) { return resolvedDependencies.entrySet().stream() .filter(entry -> Optionals.join(identifier, x -> !entry.getKey().equals(x), () -> true)) .filter(entry -> dependencies.requires(entry.getKey())) .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); } private static IO<Optional<IOException>> installDependency( final String dependenciesDirectory, final RecipeVersionIdentifier identifier, final RecipeVersion recipeVersion, final ImmutableMap<RecipeIdentifier, SemanticVersion> resolvedDependencies) { Preconditions.checkNotNull(dependenciesDirectory); Preconditions.checkNotNull(identifier); Preconditions.checkNotNull(recipeVersion); Preconditions.checkNotNull(resolvedDependencies); final ImmutableMap<RecipeIdentifier, SemanticVersion> refinedDependencies = refineDependencies( Optional.of(identifier.project), resolvedDependencies, recipeVersion.dependencies); return continueUntilPresent(ImmutableList.of( IO.println("Installing " + identifier.encode() + "... ") .then(IO.value(Optional.empty())), fetchDependency(dependenciesDirectory, identifier, recipeVersion, refinedDependencies), recipePath(dependenciesDirectory, identifier) .flatMap(path -> recipeVersion.buckResource.map( resource -> Routines.fetchRemoteFile(path + "/BUCK", resource)) .orElseGet(() -> IO.value(Optional.empty()))), recipePath(dependenciesDirectory, identifier) .flatMap(path -> generateBuckConfig(path + "/.buckconfig.local", "../../..", refinedDependencies)), recipePath(dependenciesDirectory, identifier) .flatMap(path -> generateBuckarooDeps(path + "/BUCKAROO_DEPS", refinedDependencies)))); } private static Optional<RecipeVersion> fetchRecipeVersion( final ImmutableList<CookBook> cookBooks, final RecipeVersionIdentifier identifier) { Preconditions.checkNotNull(cookBooks); Preconditions.checkNotNull(identifier); return cookBooks.stream() .flatMap(x -> x.organizations.entrySet() .stream() .flatMap(y -> y.getValue().recipes.entrySet() .stream() .flatMap(z -> z.getValue().versions.entrySet() .stream() .map(w -> Maps.immutableEntry( RecipeVersionIdentifier.of(RecipeIdentifier.of(y.getKey(), z.getKey()), w.getKey()), w.getValue()))))) .filter(x -> x.getKey().equals(identifier)) .map(Map.Entry::getValue) .findFirst(); } private static IO<Optional<IOException>> installDependencies( final String dependenciesDirectory, final ImmutableList<CookBook> cookBooks, final ImmutableMap<RecipeIdentifier, SemanticVersion> versions) { Preconditions.checkNotNull(dependenciesDirectory); Preconditions.checkNotNull(cookBooks); Preconditions.checkNotNull(versions); return continueUntilPresent(versions.entrySet().stream() .map(entry -> RecipeVersionIdentifier.of(entry.getKey(), entry.getValue())) .map(recipe -> fetchRecipeVersion(cookBooks, recipe).map( recipeVersion -> installDependency(dependenciesDirectory, recipe, recipeVersion, versions)) .orElseGet(() -> IO.value(Optional.of(new IOException("Unable to find " + recipe.encode()))))) .collect(ImmutableList.toImmutableList())); } private static IO<Unit> resolveDependencies( final Project project, final BuckarooConfig config, final ImmutableList<CookBook> cookBooks) { Preconditions.checkNotNull(project); Preconditions.checkNotNull(config); Preconditions.checkNotNull(cookBooks); final DependencyFetcher fetcher = CookbookDependencyFetcher.of(cookBooks); return DependencyResolver.resolve(project.dependencies, fetcher).join( IO::println, resolvedDependencies -> continueUntilPresent(ImmutableList.of( IO.of(x -> x.fs().workingDirectory() + "/BUCKAROO_DEPS") .flatMap(path -> generateBuckarooDeps( path, refineDependencies(Optional.empty(), resolvedDependencies, project.dependencies))), IO.of(x -> x.fs().workingDirectory() + "/.buckconfig.local") .flatMap(path -> generateBuckConfig(path, "./buckaroo", resolvedDependencies)), IO.of(x -> x.fs().workingDirectory() + "/buckaroo/") .flatMap(path -> installDependencies(path, cookBooks, resolvedDependencies)))) .flatMap(x -> Optionals.join( x, IO::println, () -> IO.println("Success! ")))); } public static IO<Unit> routine = projectFilePath .flatMap(Routines::readProject) .flatMap(x -> x.join( e -> IO.println("Error reading project file... ").then(IO.println(e)), project -> Routines.ensureConfig.flatMap(e -> Optionals.join( e, i -> IO.println("Error installing default Buckaroo config... ").then(IO.println(i)), () -> configFilePath.flatMap(Routines::readConfig).flatMap(y -> y.join( IO::println, config -> readCookBooks(config).flatMap(z -> z.join( IO::println, cookBooks -> resolveDependencies(project, config, cookBooks))))))))); }