/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.gradle.plugins.defaults.internal;
import com.liferay.gradle.plugins.LiferayAntPlugin;
import com.liferay.gradle.plugins.LiferayThemePlugin;
import com.liferay.gradle.plugins.cache.CacheExtension;
import com.liferay.gradle.plugins.cache.CachePlugin;
import com.liferay.gradle.plugins.cache.WriteDigestTask;
import com.liferay.gradle.plugins.cache.task.TaskCache;
import com.liferay.gradle.plugins.cache.task.TaskCacheApplicator;
import com.liferay.gradle.plugins.change.log.builder.BuildChangeLogTask;
import com.liferay.gradle.plugins.change.log.builder.ChangeLogBuilderPlugin;
import com.liferay.gradle.plugins.defaults.LiferayOSGiDefaultsPlugin;
import com.liferay.gradle.plugins.defaults.LiferayThemeDefaultsPlugin;
import com.liferay.gradle.plugins.defaults.internal.util.FileUtil;
import com.liferay.gradle.plugins.defaults.internal.util.GitUtil;
import com.liferay.gradle.plugins.defaults.internal.util.GradleUtil;
import com.liferay.gradle.plugins.defaults.tasks.MergeFilesTask;
import com.liferay.gradle.plugins.defaults.tasks.ReplaceRegexTask;
import com.liferay.gradle.plugins.defaults.tasks.WriteArtifactPublishCommandsTask;
import com.liferay.gradle.plugins.defaults.tasks.WritePropertiesTask;
import com.liferay.gradle.util.Validator;
import groovy.lang.Closure;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Callable;
import org.gradle.StartParameter;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.PublishArtifactSet;
import org.gradle.api.artifacts.dsl.RepositoryHandler;
import org.gradle.api.artifacts.maven.MavenDeployer;
import org.gradle.api.file.CopySpec;
import org.gradle.api.file.FileCollection;
import org.gradle.api.invocation.Gradle;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.MavenPlugin;
import org.gradle.api.plugins.MavenRepositoryHandlerConvention;
import org.gradle.api.plugins.WarPlugin;
import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.Delete;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.Upload;
import org.gradle.util.CollectionUtils;
import org.gradle.util.GUtil;
/**
* @author Andrea Di Giorgi
*/
public class LiferayRelengPlugin implements Plugin<Project> {
public static final String CLEAN_ARTIFACTS_PUBLISH_COMMANDS_TASK_NAME =
"cleanArtifactsPublishCommands";
public static final Plugin<Project> INSTANCE = new LiferayRelengPlugin();
public static final String MERGE_ARTIFACTS_PUBLISH_COMMANDS =
"mergeArtifactsPublishCommands";
public static final String PRINT_DEPENDENT_ARTIFACT_TASK_NAME =
"printDependentArtifact";
public static final String PRINT_STALE_ARTIFACT_TASK_NAME =
"printStaleArtifact";
public static final String RECORD_ARTIFACT_TASK_NAME = "recordArtifact";
public static final String UPDATE_VERSION_TASK_NAME = "updateVersion";
public static final String WRITE_ARTIFACT_PUBLISH_COMMANDS =
"writeArtifactPublishCommands";
public static File getRelengDir(File projectDir) {
File rootDir = GradleUtil.getRootDir(projectDir, _RELENG_DIR_NAME);
if (rootDir == null) {
return null;
}
File relengDir = new File(rootDir, _RELENG_DIR_NAME);
return new File(relengDir, FileUtil.relativize(projectDir, rootDir));
}
public static File getRelengDir(Project project) {
return getRelengDir(project.getProjectDir());
}
@Override
public void apply(final Project project) {
File relengDir = getRelengDir(project);
if (relengDir == null) {
return;
}
GradleUtil.applyPlugin(project, ChangeLogBuilderPlugin.class);
GradleUtil.applyPlugin(project, MavenPlugin.class);
final BuildChangeLogTask buildChangeLogTask =
(BuildChangeLogTask)GradleUtil.getTask(
project, ChangeLogBuilderPlugin.BUILD_CHANGE_LOG_TASK_NAME);
final WritePropertiesTask recordArtifactTask = _addTaskRecordArtifact(
project, relengDir);
Delete cleanArtifactsPublishCommandsTask =
_addRootTaskCleanArtifactsPublishCommands(project.getGradle());
MergeFilesTask mergeArtifactsPublishCommandsTask =
_addRootTaskMergeArtifactsPublishCommands(
cleanArtifactsPublishCommandsTask);
WriteArtifactPublishCommandsTask writeArtifactPublishCommandsTask =
_addTaskWriteArtifactPublishCommands(
project, recordArtifactTask, cleanArtifactsPublishCommandsTask,
mergeArtifactsPublishCommandsTask);
mergeArtifactsPublishCommandsTask.mustRunAfter(
writeArtifactPublishCommandsTask);
_addTaskPrintStaleArtifact(project, recordArtifactTask);
_addTaskPrintDependentArtifact(project);
_configureTaskBuildChangeLog(buildChangeLogTask, relengDir);
_configureTaskUploadArchives(project, recordArtifactTask);
GradleUtil.withPlugin(
project, JavaPlugin.class,
new Action<JavaPlugin>() {
@Override
public void execute(JavaPlugin javaPlugin) {
_configureTaskProcessResources(project, buildChangeLogTask);
}
});
}
private LiferayRelengPlugin() {
}
private Delete _addRootTaskCleanArtifactsPublishCommands(Gradle gradle) {
StartParameter startParameter = gradle.getStartParameter();
Project project = GradleUtil.getProject(
gradle.getRootProject(), startParameter.getCurrentDir());
TaskContainer taskContainer = project.getTasks();
Delete delete = (Delete)taskContainer.findByName(
CLEAN_ARTIFACTS_PUBLISH_COMMANDS_TASK_NAME);
if (delete != null) {
return delete;
}
delete = GradleUtil.addTask(
project, CLEAN_ARTIFACTS_PUBLISH_COMMANDS_TASK_NAME, Delete.class);
delete.delete(
new File(project.getBuildDir(), "artifacts-publish-commands"));
delete.setDescription(
"Deletes the temporary directory that contains the artifacts " +
"publish commands.");
return delete;
}
private MergeFilesTask _addRootTaskMergeArtifactsPublishCommands(
Delete cleanArtifactsPublishCommandsTask) {
Project rootProject = cleanArtifactsPublishCommandsTask.getProject();
TaskContainer taskContainer = rootProject.getTasks();
MergeFilesTask mergeFilesTask =
(MergeFilesTask)taskContainer.findByName(
MERGE_ARTIFACTS_PUBLISH_COMMANDS);
if (mergeFilesTask != null) {
return mergeFilesTask;
}
mergeFilesTask = GradleUtil.addTask(
rootProject, MERGE_ARTIFACTS_PUBLISH_COMMANDS,
MergeFilesTask.class);
File dir = GradleUtil.toFile(
rootProject,
CollectionUtils.first(
cleanArtifactsPublishCommandsTask.getDelete()));
mergeFilesTask.doLast(
new Action<Task>() {
@Override
public void execute(Task task) {
MergeFilesTask mergeFilesTask = (MergeFilesTask)task;
Logger logger = mergeFilesTask.getLogger();
File file = mergeFilesTask.getOutputFile();
if (file.exists()) {
boolean success = file.setExecutable(true);
if (!success) {
logger.error(
"Unable to set the owner's execute " +
"permission for {}",
file);
}
if (logger.isLifecycleEnabled()) {
logger.lifecycle(
"Artifacts publish commands written in {}.",
file);
}
}
else {
if (logger.isLifecycleEnabled()) {
logger.lifecycle(
"No artifacts publish commands are available.");
}
}
}
});
mergeFilesTask.setDescription("Merges the artifacts publish commands.");
mergeFilesTask.setHeader(
"#!/bin/bash" + System.lineSeparator() + System.lineSeparator() +
"set -e" + System.lineSeparator());
mergeFilesTask.setInputFiles(
new File(dir, WRITE_ARTIFACT_PUBLISH_COMMANDS + "-step1.sh"),
new File(dir, WRITE_ARTIFACT_PUBLISH_COMMANDS + "-step2.sh"),
new File(dir, WRITE_ARTIFACT_PUBLISH_COMMANDS + "-step3.sh"));
mergeFilesTask.setOutputFile(
new File(dir, "artifacts-publish-commands.sh"));
return mergeFilesTask;
}
private Task _addTaskPrintDependentArtifact(Project project) {
Task task = project.task(PRINT_DEPENDENT_ARTIFACT_TASK_NAME);
task.doLast(
new Action<Task>() {
@Override
public void execute(Task task) {
Project project = task.getProject();
File projectDir = project.getProjectDir();
System.out.println(projectDir.getAbsolutePath());
}
});
task.onlyIf(
new Spec<Task>() {
@Override
public boolean isSatisfiedBy(Task task) {
Project project = task.getProject();
if (!GradleUtil.isTestProject(project) &&
_hasProjectDependencies(project)) {
return true;
}
return false;
}
});
task.setDescription(
"Prints the project directory if this project contains " +
"dependencies to other projects.");
task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
return task;
}
private Task _addTaskPrintStaleArtifact(
Project project, final WritePropertiesTask recordArtifactTask) {
final Task task = project.task(PRINT_STALE_ARTIFACT_TASK_NAME);
task.doLast(
new Action<Task>() {
@Override
public void execute(Task task) {
Project project = task.getProject();
File projectDir = project.getProjectDir();
System.out.println(projectDir.getAbsolutePath());
}
});
task.setDescription(
"Prints the project directory if this project has been changed " +
"since the last publish.");
task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
_configureTaskEnabledIfStale(task, recordArtifactTask);
GradleUtil.withPlugin(
project, LiferayOSGiDefaultsPlugin.class,
new Action<LiferayOSGiDefaultsPlugin>() {
@Override
public void execute(
LiferayOSGiDefaultsPlugin liferayOSGiDefaultsPlugin) {
_configureTaskPrintStaleArtifactForOSGi(task);
}
});
return task;
}
private WritePropertiesTask _addTaskRecordArtifact(
Project project, File destinationDir) {
final WritePropertiesTask writePropertiesTask = GradleUtil.addTask(
project, RECORD_ARTIFACT_TASK_NAME, WritePropertiesTask.class);
writePropertiesTask.property(
"artifact.git.id",
new Callable<String>() {
@Override
public String call() throws Exception {
return GitUtil.getGitResult(
writePropertiesTask.getProject(), "rev-parse", "HEAD");
}
});
writePropertiesTask.setDescription(
"Records the commit ID and the artifact URLs.");
writePropertiesTask.setOutputFile(
new File(destinationDir, "artifact.properties"));
Configuration configuration = GradleUtil.getConfiguration(
project, Dependency.ARCHIVES_CONFIGURATION);
PublishArtifactSet publishArtifactSet = configuration.getArtifacts();
Action<PublishArtifact> action = new Action<PublishArtifact>() {
@Override
public void execute(final PublishArtifact publishArtifact) {
writePropertiesTask.property(
new Callable<String>() {
@Override
public String call() throws Exception {
String key = publishArtifact.getClassifier();
if (Validator.isNull(key)) {
key = publishArtifact.getType();
Project project =
writePropertiesTask.getProject();
if ((JavaPlugin.JAR_TASK_NAME.equals(key) &&
GradleUtil.hasPlugin(
project, JavaPlugin.class)) ||
(WarPlugin.WAR_TASK_NAME.equals(key) &&
(GradleUtil.hasPlugin(
project, LiferayAntPlugin.class) ||
GradleUtil.hasPlugin(
project,
LiferayThemePlugin.class)))) {
key = null;
}
}
if (Validator.isNull(key)) {
key = "artifact.url";
}
else {
key = "artifact." + key + ".url";
}
return key;
}
},
new Callable<String>() {
@Override
public String call() throws Exception {
return _getArtifactRemoteURL(
writePropertiesTask.getProject(),
publishArtifact, false);
}
});
}
};
publishArtifactSet.all(action);
return writePropertiesTask;
}
private WriteArtifactPublishCommandsTask
_addTaskWriteArtifactPublishCommands(
Project project, final WritePropertiesTask recordArtifactTask,
Delete cleanArtifactsPublishCommandsTask,
MergeFilesTask mergeArtifactsPublishCommandsTask) {
final WriteArtifactPublishCommandsTask
writeArtifactPublishCommandsTask = GradleUtil.addTask(
project, WRITE_ARTIFACT_PUBLISH_COMMANDS,
WriteArtifactPublishCommandsTask.class);
writeArtifactPublishCommandsTask.dependsOn(
cleanArtifactsPublishCommandsTask);
writeArtifactPublishCommandsTask.doFirst(
new Action<Task>() {
@Override
public void execute(Task task) {
Project project = task.getProject();
Gradle gradle = project.getGradle();
StartParameter startParameter = gradle.getStartParameter();
if (startParameter.isParallelProjectExecutionEnabled()) {
throw new GradleException(
"Unable to run " + task + " in parallel");
}
}
});
writeArtifactPublishCommandsTask.finalizedBy(
mergeArtifactsPublishCommandsTask);
writeArtifactPublishCommandsTask.setArtifactPropertiesFile(
new Callable<File>() {
@Override
public File call() throws Exception {
return recordArtifactTask.getOutputFile();
}
});
writeArtifactPublishCommandsTask.setDescription(
"Prints the artifact publish commands if this project has been " +
"changed since the last publish.");
writeArtifactPublishCommandsTask.setOutputDir(
CollectionUtils.first(
cleanArtifactsPublishCommandsTask.getDelete()));
_configureTaskEnabledIfStale(
writeArtifactPublishCommandsTask, recordArtifactTask);
String projectPath = project.getPath();
if (projectPath.startsWith(":apps:") ||
projectPath.startsWith(":private:apps:") ||
projectPath.startsWith(":private:util:") ||
projectPath.startsWith(":util:")) {
writeArtifactPublishCommandsTask.onlyIf(
new Spec<Task>() {
@Override
public boolean isSatisfiedBy(Task task) {
if (_hasProjectDependencies(task.getProject())) {
return false;
}
return true;
}
});
_configureTaskEnabledIfDependenciesArePublished(
writeArtifactPublishCommandsTask);
}
GradleUtil.withPlugin(
project, LiferayOSGiDefaultsPlugin.class,
new Action<LiferayOSGiDefaultsPlugin>() {
@Override
public void execute(
LiferayOSGiDefaultsPlugin liferayOSGiDefaultsPlugin) {
_configureTaskWriteArtifactPublishCommandsForOSGi(
writeArtifactPublishCommandsTask);
}
});
project.afterEvaluate(
new Action<Project>() {
@Override
public void execute(Project project) {
TaskContainer taskContainer = project.getTasks();
Task task = taskContainer.findByName(
UPDATE_VERSION_TASK_NAME);
if (task instanceof ReplaceRegexTask) {
ReplaceRegexTask replaceRegexTask =
(ReplaceRegexTask)task;
Map<String, FileCollection> matches =
replaceRegexTask.getMatches();
writeArtifactPublishCommandsTask.prepNextFiles(
matches.values());
}
if (GradleUtil.hasPlugin(project, CachePlugin.class)) {
CacheExtension cacheExtension = GradleUtil.getExtension(
project, CacheExtension.class);
for (TaskCache taskCache : cacheExtension.getTasks()) {
writeArtifactPublishCommandsTask.prepNextFiles(
new File(
taskCache.getCacheDir(),
TaskCacheApplicator.DIGEST_FILE_NAME));
}
}
if (GradleUtil.hasPlugin(
project, LiferayThemeDefaultsPlugin.class)) {
WriteDigestTask writeDigestTask =
(WriteDigestTask)GradleUtil.getTask(
project,
LiferayThemeDefaultsPlugin.
WRITE_PARENT_THEMES_DIGEST_TASK_NAME);
writeArtifactPublishCommandsTask.prepNextCommitFile(
"digest", writeDigestTask.getDigestFile());
}
}
});
return writeArtifactPublishCommandsTask;
}
private void _configureTaskBuildChangeLog(
BuildChangeLogTask buildChangeLogTask, File destinationDir) {
buildChangeLogTask.setChangeLogFile(
new File(destinationDir, "liferay-releng.changelog"));
}
private void _configureTaskEnabledIfDependenciesArePublished(Task task) {
task.onlyIf(
new Spec<Task>() {
@Override
public boolean isSatisfiedBy(Task task) {
try {
Project project = task.getProject();
if (FileUtil.contains(
project.getBuildFile(),
"version: \"default\"")) {
return false;
}
return true;
}
catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
});
}
private void _configureTaskEnabledIfRelease(Task task) {
task.onlyIf(
new Spec<Task>() {
@Override
public boolean isSatisfiedBy(Task task) {
Project project = task.getProject();
if (GradleUtil.hasStartParameterTask(
project, task.getName()) ||
!GradleUtil.isSnapshot(project)) {
return true;
}
return false;
}
});
}
private void _configureTaskEnabledIfStale(
Task task, final WritePropertiesTask recordArtifactTask) {
String force = GradleUtil.getTaskPrefixedProperty(task, "force");
if (Boolean.parseBoolean(force)) {
return;
}
task.onlyIf(
new Spec<Task>() {
@Override
public boolean isSatisfiedBy(Task task) {
if (FileUtil.exists(
task.getProject(), ".lfrbuild-releng-ignore")) {
return false;
}
return true;
}
});
task.onlyIf(
new Spec<Task>() {
@Override
public boolean isSatisfiedBy(Task task) {
Properties artifactProperties;
File artifactPropertiesFile =
recordArtifactTask.getOutputFile();
if (artifactPropertiesFile.exists()) {
artifactProperties = GUtil.loadProperties(
artifactPropertiesFile);
}
else {
artifactProperties = new Properties();
}
return _isStale(
recordArtifactTask.getProject(), artifactProperties);
}
});
}
private void _configureTaskPrintStaleArtifactForOSGi(Task task) {
if (GradleUtil.isTestProject(task.getProject())) {
task.setEnabled(false);
}
}
private void _configureTaskProcessResources(
Project project, final BuildChangeLogTask buildChangeLogTask) {
Copy copy = (Copy)GradleUtil.getTask(
project, JavaPlugin.PROCESS_RESOURCES_TASK_NAME);
copy.from(
new Callable<File>() {
@Override
public File call() throws Exception {
return buildChangeLogTask.getChangeLogFile();
}
},
new Closure<Void>(project) {
@SuppressWarnings("unused")
public void doCall(CopySpec copySpec) {
copySpec.into("META-INF");
}
});
}
private void _configureTaskUploadArchives(
Project project, Task recordArtifactTask) {
Task uploadArchivesTask = GradleUtil.getTask(
project, BasePlugin.UPLOAD_ARCHIVES_TASK_NAME);
uploadArchivesTask.dependsOn(recordArtifactTask);
_configureTaskEnabledIfRelease(recordArtifactTask);
}
private void _configureTaskWriteArtifactPublishCommandsForOSGi(
WriteArtifactPublishCommandsTask writeArtifactPublishCommandsTask) {
Project project = writeArtifactPublishCommandsTask.getProject();
if (GradleUtil.isTestProject(project)) {
writeArtifactPublishCommandsTask.setEnabled(false);
}
writeArtifactPublishCommandsTask.setFirstPublishExcludedTaskName(
LiferayOSGiDefaultsPlugin.UPDATE_FILE_VERSIONS_TASK_NAME);
}
private StringBuilder _getArtifactRemoteBaseURL(
Project project, boolean cdn)
throws Exception {
Upload upload = (Upload)GradleUtil.getTask(
project, BasePlugin.UPLOAD_ARCHIVES_TASK_NAME);
RepositoryHandler repositoryHandler = upload.getRepositories();
MavenDeployer mavenDeployer = (MavenDeployer)repositoryHandler.getAt(
MavenRepositoryHandlerConvention.DEFAULT_MAVEN_DEPLOYER_NAME);
Object repository = mavenDeployer.getRepository();
// org.apache.maven.artifact.ant.RemoteRepository is not in the
// classpath
Class<?> repositoryClass = repository.getClass();
Method getUrlMethod = repositoryClass.getMethod("getUrl");
String url = (String)getUrlMethod.invoke(repository);
if (cdn) {
url = url.replace("http://", "http://cdn.");
url = url.replace("https://", "https://cdn.");
}
StringBuilder sb = new StringBuilder(url);
if (sb.charAt(sb.length() - 1) != '/') {
sb.append('/');
}
String group = String.valueOf(project.getGroup());
sb.append(group.replace('.', '/'));
sb.append('/');
return sb;
}
private String _getArtifactRemoteURL(
Project project, PublishArtifact publishArtifact, boolean cdn)
throws Exception {
StringBuilder sb = _getArtifactRemoteBaseURL(project, cdn);
String name = GradleUtil.getArchivesBaseName(project);
sb.append(name);
sb.append('/');
sb.append(project.getVersion());
sb.append('/');
sb.append(name);
sb.append('-');
sb.append(project.getVersion());
String classifier = publishArtifact.getClassifier();
if (Validator.isNotNull(classifier)) {
sb.append('-');
sb.append(classifier);
}
sb.append('.');
sb.append(publishArtifact.getExtension());
return sb.toString();
}
private boolean _hasProjectDependencies(Project project) {
for (Configuration configuration : project.getConfigurations()) {
for (Dependency dependency : configuration.getDependencies()) {
if (dependency instanceof ProjectDependency) {
return true;
}
}
}
return false;
}
private boolean _isStale(
final Project project, Properties artifactProperties) {
Logger logger = project.getLogger();
final String artifactGitId = artifactProperties.getProperty(
"artifact.git.id");
if (Validator.isNull(artifactGitId)) {
if (logger.isInfoEnabled()) {
logger.info("{} has never been published", project);
}
return true;
}
String result = GitUtil.getGitResult(
project, "log", "--format=%s", artifactGitId + "..HEAD", ".");
String[] lines = result.split("\\r?\\n");
for (String line : lines) {
if (logger.isInfoEnabled()) {
logger.info(line);
}
if (Validator.isNull(line)) {
continue;
}
if (!line.contains(
WriteArtifactPublishCommandsTask.IGNORED_MESSAGE_PATTERN)) {
return true;
}
}
if (GradleUtil.hasPlugin(project, LiferayThemeDefaultsPlugin.class)) {
WriteDigestTask writeDigestTask =
(WriteDigestTask)GradleUtil.getTask(
project,
LiferayThemeDefaultsPlugin.
WRITE_PARENT_THEMES_DIGEST_TASK_NAME);
String digest = writeDigestTask.getDigest();
String oldDigest = writeDigestTask.getOldDigest();
if (logger.isInfoEnabled()) {
logger.info(
"Digest for {} is {}, old digest is {}", writeDigestTask,
digest, oldDigest);
}
if (!Objects.equals(digest, oldDigest)) {
return true;
}
}
return false;
}
private static final String _RELENG_DIR_NAME = ".releng";
}