Skip to content

Commit

Permalink
Add files copy to core (#1548)
Browse files Browse the repository at this point in the history
* Adds GuideProjectGenerator to core.

* Adds GuideProjectGenerator to core.

* Adds FileCopyUtility to core.

* Adds test file.

* Adds tests.

* Fixes tests.

* Format code, optimize imports.

* Changes order of arguments in transferFiles method.
  • Loading branch information
AndreaLaGrotteria authored Nov 6, 2024
1 parent 48d136f commit 3c52f3a
Show file tree
Hide file tree
Showing 54 changed files with 808 additions and 208 deletions.
30 changes: 15 additions & 15 deletions buildSrc/src/main/java/io/micronaut/guides/core/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,30 @@
import io.micronaut.core.util.StringUtils;
import io.micronaut.jsonschema.JsonSchema;
import io.micronaut.serde.annotation.Serdeable;
import io.micronaut.starter.application.ApplicationType;
import io.micronaut.starter.api.TestFramework;
import io.micronaut.starter.application.ApplicationType;
import jakarta.validation.constraints.NotBlank;

import java.util.List;

/**
*
* @param name The app's name. For single application guides, the application needs to be named default
* @param packageName The app's package name. If you don't specify, the package name example.micronaut is used
* @param applicationType The app type. If you don't specify, default is used
* @param framework The app's framework. Default is Micronaut but Spring Boot is also supported
* @param features The Micronaut Starter features' name that the app requires
* @param name The app's name. For single application guides, the application needs to be named default
* @param packageName The app's package name. If you don't specify, the package name example.micronaut is used
* @param applicationType The app type. If you don't specify, default is used
* @param framework The app's framework. Default is Micronaut but Spring Boot is also supported
* @param features The Micronaut Starter features' name that the app requires
* @param invisibleFeatures The app's invisible features
* @param kotlinFeatures The app's Kotlin features
* @param javaFeatures The app's Java features
* @param groovyFeatures The app's Groovy features
* @param testFramework The app's test framework
* @param excludeTest The tests that should not be run
* @param excludeSource The source files that should not be included
* @param validateLicense To enable Spotless code check
* @param kotlinFeatures The app's Kotlin features
* @param javaFeatures The app's Java features
* @param groovyFeatures The app's Groovy features
* @param testFramework The app's test framework
* @param excludeTest The tests that should not be run
* @param excludeSource The source files that should not be included
* @param validateLicense To enable Spotless code check
*/
@JsonSchema
@Serdeable
public record App (
public record App(
@NonNull
@NotBlank
String name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
import io.micronaut.http.uri.UriBuilder;
import io.micronaut.starter.application.ApplicationType;
import jakarta.inject.Singleton;

import java.net.URI;
import java.util.*;
import static io.micronaut.guides.core.MacroUtils.*;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import static io.micronaut.guides.core.MacroUtils.findMacroLines;

@Singleton
public class BuildDiffLinkSubstitution implements MacroSubstitution {
public static final String MACRO_DIFF_LINK = "diffLink";
private static final String QUERY_PARAMLANG = "lang";
private static final String QUERY_PARAM_BUILD = "build";
private static final String QUERY_PARAM_TEST = "test";
Expand All @@ -19,7 +24,6 @@ public class BuildDiffLinkSubstitution implements MacroSubstitution {
private static final String QUERY_PARAM_PACKAGE = "package";
private static final String QUERY_PARAM_ACTIVITY = "activity";
private static final String QUERY_PARAM_FEATURES = "features";
public static final String MACRO_DIFF_LINK = "diffLink";
private static final String ATTRIBUTE_FEATURES = "features";
private static final String ATTRIBUTE_EXCLUDE_FEATURES = "featureExcludes";
private final GuidesConfiguration guidesConfiguration;
Expand All @@ -28,16 +32,32 @@ public BuildDiffLinkSubstitution(GuidesConfiguration config) {
this.guidesConfiguration = config;
}

private static Set<String> features(App app, AsciidocMacro asciidocMacro, GuidesOption option) {
Set<String> features = new HashSet<>();
if (app != null) {
features.addAll(GuideUtils.getAppVisibleFeatures(app, option.getLanguage()));
}
asciidocMacro.attributes().stream()
.filter(attribute -> attribute.key().equals(ATTRIBUTE_FEATURES))
.map(Attribute::values)
.forEach(features::addAll);
asciidocMacro.attributes().stream()
.filter(attribute -> attribute.key().equals(ATTRIBUTE_EXCLUDE_FEATURES))
.map(Attribute::values)
.forEach(features::removeAll);
return features;
}

@Override
public String substitute(String str, Guide guide, GuidesOption option) {
for(String line : findMacroLines(str, MACRO_DIFF_LINK)) {
for (String line : findMacroLines(str, MACRO_DIFF_LINK)) {
Optional<AsciidocMacro> asciidocMacroOptional = AsciidocMacro.of(MACRO_DIFF_LINK, line);
if (asciidocMacroOptional.isEmpty()) {
continue;
}
AsciidocMacro asciidocMacro = asciidocMacroOptional.get();
String res = buildDiffLink(asciidocMacro, guide, option).toString() + "[Diff]";
str = str.replace(line,res);
str = str.replace(line, res);
}
return str;
}
Expand All @@ -57,20 +77,4 @@ private URI buildDiffLink(AsciidocMacro asciidocMacro, Guide guide, GuidesOption
features.forEach(f -> uriBuilder.queryParam(QUERY_PARAM_FEATURES, f));
return uriBuilder.build();
}

private static Set<String> features(App app, AsciidocMacro asciidocMacro, GuidesOption option) {
Set<String> features = new HashSet<>();
if (app != null) {
features.addAll(GuideUtils.getAppVisibleFeatures(app, option.getLanguage()));
}
asciidocMacro.attributes().stream()
.filter(attribute -> attribute.key().equals(ATTRIBUTE_FEATURES))
.map(Attribute::values)
.forEach(features::addAll);
asciidocMacro.attributes().stream()
.filter(attribute -> attribute.key().equals(ATTRIBUTE_EXCLUDE_FEATURES))
.map(Attribute::values)
.forEach(features::removeAll);
return features;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.inject.Singleton;
import org.gradle.api.GradleException;

import java.util.stream.Collectors;

@Singleton
Expand All @@ -12,6 +13,17 @@ public class CliMacroSubstitution extends PlaceholderWithTargetMacroSubstitution
private static final String CLI_FUNCTION = "create-function-app";
private static final String CLI_CLI = "create-cli-app";

private static String cliCommandForApp(App app) {
return switch (app.applicationType()) {
case CLI -> CLI_CLI;
case FUNCTION -> CLI_FUNCTION;
case GRPC -> CLI_GRPC;
case MESSAGING -> CLI_MESSAGING;
case DEFAULT -> CLI_DEFAULT;
default -> throw new IllegalArgumentException("Unknown application type: " + app.applicationType());
};
}

@Override
protected String getMacroName() {
return "cli-command";
Expand All @@ -25,19 +37,8 @@ protected String getSubstitution(Guide guide, GuidesOption option, String appNam
.orElse(null);
if (app != null) {
return cliCommandForApp(app);
} else{
} else {
throw new GradleException("No CLI command found for app: " + app + " -- should be one of " + guide.apps().stream().map(el -> "@" + el + ":cli-command@").collect(Collectors.joining(", ")));
}
}

private static String cliCommandForApp(App app) {
return switch (app.applicationType()) {
case CLI -> CLI_CLI;
case FUNCTION -> CLI_FUNCTION;
case GRPC -> CLI_GRPC;
case MESSAGING -> CLI_MESSAGING;
case DEFAULT -> CLI_DEFAULT;
default -> throw new IllegalArgumentException("Unknown application type: " + app.applicationType());
};
}
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/java/io/micronaut/guides/core/Cloud.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

public enum Cloud implements Ordered {
OCI("Oracle Cloud", "OCI", 1),
AWS("Amazon Web Services", "AWS", 2),
AWS("Amazon Web Services", "AWS", 2),
AZURE("Microsoft Azure", "Azure", 3),
GCP("Google Cloud Platform", "GCP", 4);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.Map;

@Singleton
public class DefaultCoordinatesProvider implements CoordinatesProvider{
public class DefaultCoordinatesProvider implements CoordinatesProvider {
@Override
public Map<String, Coordinate> getCoordinates() {
try (ApplicationContext context = ApplicationContext.run()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package io.micronaut.guides.core;

import io.micronaut.core.annotation.NonNull;
import jakarta.inject.Singleton;
import jakarta.validation.constraints.NotNull;
import org.gradle.api.GradleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;

import static io.micronaut.core.util.StringUtils.EMPTY_STRING;

@Singleton
public class DefaultFilesTransferUtility implements FilesTransferUtility {
private static final Logger LOG = LoggerFactory.getLogger(DefaultFilesTransferUtility.class);
private static final String EXTENSION_JAVA = ".java";
private static final String EXTENSION_GROOVY = ".groovy";
private static final String EXTENSION_KT = ".kt";

private final LicenseLoader licenseLoader;
private final GuidesConfiguration guidesConfiguration;

DefaultFilesTransferUtility(LicenseLoader licenseLoader,
GuidesConfiguration guidesConfiguration) {
this.licenseLoader = licenseLoader;
this.guidesConfiguration = guidesConfiguration;
}

private static boolean fileContainsText(File file, String text) {
try {
return new String(Files.readAllBytes(file.toPath())).contains(text);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}

private static void copyGuideSourceFiles(File inputDir, Path destinationPath,
String appName, String language,
boolean ignoreMissingDirectories) throws IOException {

// look for a common 'src' directory shared by multiple languages and copy those files first
final String srcFolder = "src";
Path srcPath = Paths.get(inputDir.getAbsolutePath(), appName, srcFolder);
if (Files.exists(srcPath)) {
Files.walkFileTree(srcPath, new CopyFileVisitor(Paths.get(destinationPath.toString(), srcFolder)));
}

Path sourcePath = Paths.get(inputDir.getAbsolutePath(), appName, language);
if (!Files.exists(sourcePath)) {
sourcePath.toFile().mkdir();
}
if (Files.exists(sourcePath)) {
// copy source/resource files for the current language
Files.walkFileTree(sourcePath, new CopyFileVisitor(destinationPath));
} else if (!ignoreMissingDirectories) {
throw new GradleException("source directory " + sourcePath.toFile().getAbsolutePath() + " does not exist");
}
}

private static File fileToDelete(File destination, String path) {
return Paths.get(destination.getAbsolutePath(), path).toFile();
}

private static void copyFile(File inputDir, File destinationRoot, String filePath) throws IOException {
File sourceFile = new File(inputDir, filePath);
File destinationFile = new File(destinationRoot, filePath);

File destinationFileDir = destinationFile.getParentFile();
if (!destinationFileDir.exists()) {
Files.createDirectories(destinationFileDir.toPath());
}

Files.copy(sourceFile.toPath(), destinationFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

@Override
public void transferFiles(@NotNull @NonNull File inputDirectory, @NotNull @NonNull File outputDirectory, @NotNull @NonNull Guide guide) throws IOException {
List<GuidesOption> guidesOptionList = GuideGenerationUtils.guidesOptions(guide, LOG);
for (GuidesOption guidesOption : guidesOptionList) {
for (App app : guide.apps()) {
String appName = app.name().equals(guidesConfiguration.getDefaultAppName()) ? EMPTY_STRING : app.name();
String folder = MacroUtils.getSourceDir(guide.slug(), guidesOption);
Path destinationPath = Paths.get(outputDirectory.getAbsolutePath(), folder, appName);
File destination = destinationPath.toFile();

if (guide.base() != null) {
File baseDir = new File(inputDirectory.getParentFile(), guide.base());
copyGuideSourceFiles(baseDir, destinationPath, appName, guidesOption.getLanguage().toString(), true);
}

copyGuideSourceFiles(inputDirectory, destinationPath, appName, guidesOption.getLanguage().toString(), false);

if (app.excludeSource() != null) {
for (String mainSource : app.excludeSource()) {
File f = fileToDelete(destination, GuideGenerationUtils.mainPath(appName, mainSource, guidesOption, guidesConfiguration));
if (f.exists()) {
f.delete();
}
f = fileToDelete(destination, GuideGenerationUtils.mainPath(appName, mainSource, guidesOption, guidesConfiguration));
if (f.exists()) {
f.delete();
}
}
}

if (app.excludeTest() != null) {
for (String testSource : app.excludeTest()) {
File f = fileToDelete(destination, GuideGenerationUtils.testPath(appName, testSource, guidesOption, guidesConfiguration));
if (f.exists()) {
f.delete();
}
f = fileToDelete(destination, GuideGenerationUtils.testPath(appName, testSource, guidesOption, guidesConfiguration));
if (f.exists()) {
f.delete();
}
}
}

if (guide.zipIncludes() != null) {
File destinationRoot = new File(outputDirectory.getAbsolutePath(), folder);
for (String zipInclude : guide.zipIncludes()) {
copyFile(inputDirectory, destinationRoot, zipInclude);
}
}
addLicenses(new File(outputDirectory.getAbsolutePath(), folder));
}
}
}

void addLicenses(File folder) {
String licenseHeader = licenseLoader.getLicenseHeaderText();
Arrays.stream(folder.listFiles()).forEach(file -> {
if ((file.getPath().endsWith(EXTENSION_JAVA) || file.getPath().endsWith(EXTENSION_GROOVY) || file.getPath().endsWith(EXTENSION_KT))
&& !fileContainsText(file, "Licensed under")) {
try {
String content = new String(Files.readAllBytes(file.toPath()));
Files.write(file.toPath(), (licenseHeader + content).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public DefaultGuideParser(JsonSchemaProvider jsonSchemaProvider, JsonMapper json
public List<Guide> parseGuidesMetadata(@NonNull @NotNull File guidesDir, @NonNull @NotNull String metadataConfigName) {
List<Guide> metadatas = new ArrayList<>();

File dirs[] = guidesDir.listFiles(File::isDirectory);
if(dirs == null) {
File[] dirs = guidesDir.listFiles(File::isDirectory);
if (dirs == null) {
return metadatas;
}
for (File dir : dirs) {
Expand Down Expand Up @@ -69,10 +69,10 @@ public Optional<Guide> parseGuideMetadata(@NonNull @NotNull File guidesDir, @Non
return Optional.empty();
}

Map<String, Object> config = (Map<String,Object>) new JsonSlurper().parse(configFile);
Map<String, Object> config = (Map<String, Object>) new JsonSlurper().parse(configFile);
boolean publish = config.get("publish") == null || (Boolean) config.get("publish");

if(publish) {
if (publish) {
Set<ValidationMessage> assertions = jsonSchemaProvider.getSchema().validate(content, InputFormat.JSON);

if (!assertions.isEmpty()) {
Expand Down
Loading

0 comments on commit 3c52f3a

Please sign in to comment.