Commit 58ca9a1c authored by Andy Wilkinson's avatar Andy Wilkinson

Add a BuildInfo task for generating build.properties with Gradle

The commit adds a new BuildInfo task that can be used to generate
a build.properties file, intended for inclusion in the Actuator's
info endpoint.

A default instance of the task can be configure using the plugin's
DSL:

springBoot {
	buildInfo()
}

Additional properties can also be configured using the DSL:

springBoot {
	buildInfo {
		additionalProperties = [
			'foo': 'bar'
		]
	}
}

When configured via the DSL, the Java plugin's classes task is
configured to depend on the build info task. Alternatively, if more
control is required, the task can be declared and configured manually:

task buildInfo(type: org.springframework.boot.gradle.buildinfo.BuildInfo) {
	additionalProperties = [
		'foo': 'bar'
	]
}

classes {
	dependsOn buildInfo
}

See gh-2559
parent 41674697
...@@ -22,9 +22,11 @@ apply plugin: 'spring-boot' ...@@ -22,9 +22,11 @@ apply plugin: 'spring-boot'
jar { jar {
baseName = 'spring-boot-sample-actuator' baseName = 'spring-boot-sample-actuator'
version = '0.0.0'
} }
group = 'org.springframework.boot'
version = springBootVersion
repositories { repositories {
// NOTE: You should declare only repositories that you need here // NOTE: You should declare only repositories that you need here
mavenLocal() mavenLocal()
...@@ -59,3 +61,7 @@ springBoot { ...@@ -59,3 +61,7 @@ springBoot {
task wrapper(type: Wrapper) { task wrapper(type: Wrapper) {
gradleVersion = '1.6' gradleVersion = '1.6'
} }
springBoot {
buildInfo()
}
\ No newline at end of file
...@@ -39,7 +39,8 @@ public class SpringBootPlugin implements Plugin<Project> { ...@@ -39,7 +39,8 @@ public class SpringBootPlugin implements Plugin<Project> {
@Override @Override
public void apply(Project project) { public void apply(Project project) {
project.getExtensions().create("springBoot", SpringBootPluginExtension.class); project.getExtensions().create("springBoot", SpringBootPluginExtension.class,
project);
project.getPlugins().apply(JavaPlugin.class); project.getPlugins().apply(JavaPlugin.class);
new AgentPluginFeatures().apply(project); new AgentPluginFeatures().apply(project);
new RepackagePluginFeatures().apply(project); new RepackagePluginFeatures().apply(project);
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -20,6 +20,11 @@ import java.io.File; ...@@ -20,6 +20,11 @@ import java.io.File;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import groovy.lang.Closure;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.springframework.boot.gradle.buildinfo.BuildInfo;
import org.springframework.boot.loader.tools.Layout; import org.springframework.boot.loader.tools.Layout;
import org.springframework.boot.loader.tools.Layouts; import org.springframework.boot.loader.tools.Layouts;
...@@ -39,9 +44,12 @@ import org.springframework.boot.loader.tools.Layouts; ...@@ -39,9 +44,12 @@ import org.springframework.boot.loader.tools.Layouts;
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson
*/ */
public class SpringBootPluginExtension { public class SpringBootPluginExtension {
private final Project project;
/** /**
* The main class that should be run. Instead of setting this explicitly you can use * The main class that should be run. Instead of setting this explicitly you can use
* the 'mainClassName' of the project or the 'main' of the 'run' task. If not * the 'mainClassName' of the project or the 'main' of the 'run' task. If not
...@@ -128,6 +136,10 @@ public class SpringBootPluginExtension { ...@@ -128,6 +136,10 @@ public class SpringBootPluginExtension {
*/ */
Map<String, String> embeddedLaunchScriptProperties; Map<String, String> embeddedLaunchScriptProperties;
public SpringBootPluginExtension(Project project) {
this.project = project;
}
/** /**
* Convenience method for use in a custom task. * Convenience method for use in a custom task.
* @return the Layout to use or null if not explicitly set * @return the Layout to use or null if not explicitly set
...@@ -249,6 +261,21 @@ public class SpringBootPluginExtension { ...@@ -249,6 +261,21 @@ public class SpringBootPluginExtension {
this.embeddedLaunchScriptProperties = embeddedLaunchScriptProperties; this.embeddedLaunchScriptProperties = embeddedLaunchScriptProperties;
} }
public void buildInfo() {
this.buildInfo(null);
}
public void buildInfo(Closure<?> taskConfigurer) {
BuildInfo buildInfo = this.project.getTasks().create("buildInfo",
BuildInfo.class);
this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME)
.dependsOn(buildInfo);
if (taskConfigurer != null) {
taskConfigurer.setDelegate(buildInfo);
taskConfigurer.call();
}
}
/** /**
* Layout Types. * Layout Types.
*/ */
......
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.gradle.buildinfo;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.gradle.api.DefaultTask;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.TaskExecutionException;
import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.loader.tools.BuildPropertiesWriter;
import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails;
/**
* {@link DefaultTask} for generating a {@code build.properties} file from a
* {@code Project}.
* <p>
* By default, the {@code build.properties} file is generated in
* project.buildDir/resources/main/META-INF/boot.
* </p>
*
* @author Andy Wilkinson
*/
public class BuildInfo extends DefaultTask {
@OutputFile
private File outputFile = getProject().file(new File(getProject().getBuildDir(),
"resources/main/META-INF/boot/build.properties"));
@Input
private String projectGroup = getProject().getGroup().toString();
@Input
private String projectArtifact = ((Jar) getProject().getTasks()
.getByName(JavaPlugin.JAR_TASK_NAME)).getBaseName();
@Input
private String projectVersion = getProject().getVersion().toString();
@Input
private String projectName = getProject().getName();
@Input
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@TaskAction
public void generateBuildProperties() {
try {
new BuildPropertiesWriter(this.outputFile)
.writeBuildProperties(new ProjectDetails(this.projectGroup,
this.projectArtifact, this.projectVersion, this.projectName,
coerceToStringValues(this.additionalProperties)));
}
catch (IOException ex) {
throw new TaskExecutionException(this, ex);
}
}
public String getProjectGroup() {
return this.projectGroup;
}
public void setProjectGroup(String projectGroup) {
this.projectGroup = projectGroup;
}
public String getProjectArtifact() {
return this.projectArtifact;
}
public void setProjectArtifact(String projectArtifact) {
this.projectArtifact = projectArtifact;
}
public String getProjectVersion() {
return this.projectVersion;
}
public void setProjectVersion(String projectVersion) {
this.projectVersion = projectVersion;
}
public String getProjectName() {
return this.projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public File getOutputFile() {
return this.outputFile;
}
public void setOutputFile(File outputFile) {
this.outputFile = outputFile;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}
private Map<String, String> coerceToStringValues(Map<String, Object> input) {
Map<String, String> output = new HashMap<String, String>();
for (Entry<String, Object> entry : this.additionalProperties.entrySet()) {
output.put(entry.getKey(), entry.getValue().toString());
}
return output;
}
}
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.loader.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
/**
* A {@code BuildPropertiesWriter} writes the {@code build.properties} for consumption by
* the Actuator.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
public final class BuildPropertiesWriter {
private final File outputFile;
/**
* Creates a new {@code BuildPropertiesWriter} that will write to the given
* {@code outputFile}.
*
* @param outputFile the output file
*/
public BuildPropertiesWriter(File outputFile) {
this.outputFile = outputFile;
}
public void writeBuildProperties(ProjectDetails projectDetails) throws IOException {
Properties properties = createBuildInfo(projectDetails);
createFileIfNecessary(this.outputFile);
FileOutputStream outputStream = new FileOutputStream(this.outputFile);
try {
properties.store(outputStream, "Properties");
}
finally {
try {
outputStream.close();
}
catch (IOException ex) {
// Continue
}
}
}
private void createFileIfNecessary(File file) throws IOException {
if (file.exists()) {
return;
}
File parent = file.getParentFile();
if (!parent.isDirectory() && !parent.mkdirs()) {
throw new IllegalStateException("Cannot create parent directory for '"
+ this.outputFile.getAbsolutePath() + "'");
}
if (!file.createNewFile()) {
throw new IllegalStateException("Cannot create target file '"
+ this.outputFile.getAbsolutePath() + "'");
}
}
protected Properties createBuildInfo(ProjectDetails project) {
Properties properties = new Properties();
properties.put("build.group", project.getGroup());
properties.put("build.artifact", project.getArtifact());
properties.put("build.name", project.getName());
properties.put("build.version", project.getVersion());
properties.put("build.time", formatDate(new Date()));
if (project.getAdditionalProperties() != null) {
for (Map.Entry<String, String> entry : project.getAdditionalProperties()
.entrySet()) {
properties.put("build." + entry.getKey(), entry.getValue());
}
}
return properties;
}
private String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
return sdf.format(date);
}
/**
* Build-system agnostic details of a project.
*/
public static final class ProjectDetails {
private final String group;
private final String artifact;
private final String name;
private final String version;
private final Map<String, String> additionalProperties;
public ProjectDetails(String group, String artifact, String version, String name,
Map<String, String> additionalProperties) {
this.group = group;
this.artifact = artifact;
this.name = name;
this.version = version;
this.additionalProperties = additionalProperties;
}
public String getGroup() {
return this.group;
}
public String getArtifact() {
return this.artifact;
}
public String getName() {
return this.name;
}
public String getVersion() {
return this.version;
}
public Map<String, String> getAdditionalProperties() {
return this.additionalProperties;
}
}
}
...@@ -17,14 +17,7 @@ ...@@ -17,14 +17,7 @@
package org.springframework.boot.maven; package org.springframework.boot.maven;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
...@@ -34,6 +27,9 @@ import org.apache.maven.plugins.annotations.Mojo; ...@@ -34,6 +27,9 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.springframework.boot.loader.tools.BuildPropertiesWriter;
import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails;
/** /**
* Generate a {@code build.properties} file based the content of the current * Generate a {@code build.properties} file based the content of the current
* {@link MavenProject}. * {@link MavenProject}.
...@@ -65,68 +61,15 @@ public class BuildInfoMojo extends AbstractMojo { ...@@ -65,68 +61,15 @@ public class BuildInfoMojo extends AbstractMojo {
@Override @Override
public void execute() throws MojoExecutionException, MojoFailureException { public void execute() throws MojoExecutionException, MojoFailureException {
Properties properties = createBuildInfo();
try { try {
createFileIfNecessary(this.outputFile); new BuildPropertiesWriter(this.outputFile)
FileOutputStream outputStream = new FileOutputStream(this.outputFile); .writeBuildProperties(new ProjectDetails(this.project.getGroupId(),
try { this.project.getArtifactId(), this.project.getVersion(),
properties.store(outputStream, "Properties"); this.project.getName(), this.additionalProperties));
}
finally {
closeQuietly(outputStream);
}
} }
catch (FileNotFoundException ex) { catch (Exception ex) {
throw new MojoExecutionException(ex.getMessage(), ex); throw new MojoExecutionException(ex.getMessage(), ex);
} }
catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
private void closeQuietly(OutputStream outputStream) {
try {
outputStream.close();
}
catch (IOException ex) {
getLog().error("Error closing FileOutputStream: " + outputStream);
}
}
protected Properties createBuildInfo() {
Properties properties = new Properties();
properties.put("build.group", this.project.getGroupId());
properties.put("build.artifact", this.project.getArtifactId());
properties.put("build.name", this.project.getName());
properties.put("build.version", this.project.getVersion());
properties.put("build.time", formatDate(new Date()));
if (this.additionalProperties != null) {
for (Map.Entry<String, String> entry : this.additionalProperties.entrySet()) {
properties.put("build." + entry.getKey(), entry.getValue());
}
}
return properties;
}
private String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
return sdf.format(date);
}
private void createFileIfNecessary(File file)
throws MojoExecutionException, IOException {
if (file.exists()) {
return;
}
File parent = file.getParentFile();
if (!parent.exists() && !parent.mkdirs()) {
throw new MojoExecutionException("Cannot create parent directory for '"
+ this.outputFile.getAbsolutePath() + "'");
}
if (!file.createNewFile()) {
throw new MojoExecutionException("Cannot create target file '"
+ this.outputFile.getAbsolutePath() + "'");
}
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment