hibernate-orm/release/release.gradle

675 lines
22 KiB
Groovy

import java.nio.charset.StandardCharsets
import groovy.json.JsonSlurper
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
apply from: rootProject.file( 'gradle/base-information.gradle' )
apply plugin: 'idea'
apply plugin: 'distribution'
//apply plugin: 'java-library-distribution'
idea.module {
}
final File documentationDir = mkdir( "${project.buildDir}/documentation" )
final File projectTemplateStagingDir = mkdir( "${project.buildDir}/projectTemplate" )
configurations {
core
// testing
envers
agroal
c3p0
hikaricp
proxool
vibur
jcache
jpamodelgen
coreJakarta
// testingJakarta
enversJakarta
jpamodelgenJakarta
}
dependencies {
core project( ':hibernate-core' )
// testing project( ':hibernate-testing' )
envers project( ':hibernate-envers' )
agroal project( ':hibernate-agroal' )
c3p0 project( ':hibernate-c3p0' )
hikaricp project( ':hibernate-hikaricp' )
proxool project( ':hibernate-proxool' )
vibur project( ':hibernate-vibur' )
jcache project( ':hibernate-jcache' )
jpamodelgen project( ':hibernate-jpamodelgen' )
coreJakarta project( ':hibernate-core-jakarta' )
// testingJakarta project( ':hibernate-testing-jakarta' )
enversJakarta project( ':hibernate-envers-jakarta' )
jpamodelgenJakarta project( ':hibernate-jpamodelgen-jakarta' )
}
/**
* Assembles all documentation into the {buildDir}/documentation directory.
*
* Depends on building the docs
*/
task assembleDocumentation {
group 'Release'
description 'Assembles all documentation into the {buildDir}/documentation directory'
dependsOn rootProject.project( 'documentation' ).tasks.buildDocsForPublishing
doLast {
// copy documentation outputs into target/documentation.
// * this is used in building the dist bundles
// * it is also used as a base to build a staged directory for documentation upload
// Integrations Guide
copy {
from "${rootProject.project( 'documentation' ).buildDir}/asciidoc/integrationguide"
into "${documentationDir}/integrationguide"
}
// Getting-started Guide
copy {
from "${rootProject.project( 'documentation' ).buildDir}/asciidoc/quickstart"
into "${documentationDir}/quickstart"
}
// Topical Guide
copy {
from "${rootProject.project( 'documentation' ).buildDir}/asciidoc/topical"
into "${documentationDir}/topical"
}
// User Guide
copy {
from "${rootProject.project( 'documentation' ).buildDir}/asciidoc/userguide"
into "${documentationDir}/userguide"
}
// Aggregated JavaDoc
copy {
from "${rootProject.project( 'documentation' ).buildDir}/javadocs"
into "${documentationDir}/javadocs"
}
}
}
//task assembleProjectTemplates(type:Copy, dependsOn: project( ":project-template" ).tasks.assembleDist) {
// def templateProject = project( ":project-template" )
// from templateProject.layout.buildDirectory.dir( "distributions" )
// into projectTemplateStagingDir
//}
/**
* Upload the documentation to the JBoss doc server
*/
task uploadDocumentation(type:Exec) {
group 'Release'
description 'Uploads documentation to the JBoss doc server'
dependsOn assembleDocumentation
final String url = "filemgmt.jboss.org:/docs_htdocs/hibernate/orm/${rootProject.ormVersion.family}";
executable 'rsync'
args '-avz', '--links', '--protocol=28', "${documentationDir.absolutePath}/", url
doFirst {
if ( rootProject.ormVersion.isSnapshot ) {
logger.error( "Cannot perform upload of SNAPSHOT documentation" );
throw new RuntimeException( "Cannot perform upload of SNAPSHOT documentation" );
}
else {
logger.lifecycle( "Uploading documentation [{$url}]..." )
}
}
doLast {
logger.lifecycle( 'Done uploading documentation' )
}
}
/**
* Configuration of the distribution plugin, used to build release bundle as both ZIP and TGZ
*/
distributions {
main {
distributionBaseName = 'hibernate-release'
contents {
from rootProject.file( 'lgpl.txt' )
from rootProject.file( 'changelog.txt' )
from rootProject.file( 'hibernate_logo.gif' )
into('lib/required') {
from configurations.core
}
// todo (6.0) - add back
// into( 'project-template' ) {
// // todo : hook in some form of variable replacement - especially for version
// from project( ':project-template' ).files( 'src/main/dist' )
// }
// todo (6.0) - add back spatial
// into( 'lib/spatial' ) {
// from(
// ( parent.project( 'hibernate-spatial' ).configurations.archives.allArtifacts.files.filter{ file -> !file.name.endsWith('-sources.jar') }
// + parent.project( 'hibernate-spatial' ).configurations.runtime )
// - parent.project( 'hibernate-core' ).configurations.runtime
// - parent.project( 'hibernate-core' ).configurations.archives.allArtifacts.files
// )
// }
into( 'lib/jpa-metamodel-generator' ) {
from( configurations.jpamodelgen - configurations.core )
}
into( 'lib/envers' ) {
from( configurations.envers - configurations.core )
}
// into( 'lib/testing' ) {
// from( configurations.testing - configurations.core )
// }
into( 'lib/optional' ) {
into( 'agroal' ) {
from( configurations.agroal - configurations.core )
}
into( 'c3p0' ) {
from( configurations.c3p0 - configurations.core )
}
into( 'hikaricp' ) {
from( configurations.hikaricp - configurations.core )
}
into( 'jcache' ) {
from( configurations.jcache - configurations.core )
}
into( 'proxool' ) {
from( configurations.proxool - configurations.core )
}
into( 'vibur' ) {
from( configurations.vibur - configurations.core )
}
}
into( 'lib/jakarta' ) {
into( 'required' ) {
from configurations.coreJakarta
}
// into( 'testing' ) {
// from( configurations.testingJakarta - configurations.coreJakarta )
// }
into( 'envers' ) {
from( configurations.enversJakarta - configurations.coreJakarta )
}
into( 'jpa-metamodel-generator' ) {
from( configurations.jpamodelgenJakarta - configurations.coreJakarta )
}
}
into('documentation') {
from documentationDir
}
into( 'project' ) {
from ( rootProject.projectDir ) {
exclude( '.git' )
exclude( '.gitignore' )
exclude( 'changelog.txt' )
exclude( 'lgpl.txt' )
exclude( 'hibernate_logo.gif' )
exclude( 'tagRelease.sh' )
exclude( 'gradlew' )
exclude( 'gradlew.bat' )
exclude( 'wrapper/*' )
exclude( '**/.gradle/**' )
exclude( '**/target/**' )
exclude( '.idea' )
exclude( '**/*.ipr' )
exclude( '**/*.iml' )
exclude( '**/*.iws' )
exclude( '**/atlassian-ide-plugin.xml' )
exclude( '**/.classpath' )
exclude( '**/.project' )
exclude( '**/.settings' )
exclude( '**/.nbattrs' )
exclude( '**/out/**' )
exclude( '**/bin/**' )
exclude( 'build/**' )
exclude( '*/build/**' )
}
}
}
}
}
distTar.compression = Compression.GZIP
distTar.dependsOn assembleDocumentation
distZip.dependsOn assembleDocumentation
/**
* "virtual" task for building both types of dist bundles
*/
task buildBundles {
group 'Release'
description 'Builds all release bundles'
dependsOn distZip
dependsOn distTar
}
task uploadBundlesSourceForge(type: Exec) {
group 'Release'
description 'Uploads release bundles to SourceForge'
dependsOn buildBundles
final String url = "frs.sourceforge.net:/home/frs/project/hibernate/hibernate-orm/${version}";
executable 'rsync'
args '-vr', '-e ssh', "${project.buildDir}/distributions/", url
doFirst {
if ( rootProject.ormVersion.isSnapshot ) {
logger.error( "Cannot perform upload of SNAPSHOT bundles to SourceForge" );
throw new RuntimeException( "Cannot perform upload of SNAPSHOT bundles to SourceForge" )
}
else {
logger.lifecycle( "Uploading release bundles to SourceForge..." )
}
}
doLast {
logger.lifecycle( 'Done uploading release bundles to SourceForge' )
}
}
configurations {
bundles {
description = 'Configuration used to group the archives output from the distribution plugin.'
}
}
artifacts {
bundles distTar
bundles distZip
}
task releaseChecks {
group 'Release'
description 'Checks and preparation for release'
}
task changeLogFile {
group 'Release'
description 'Updates the changelog.txt file based on the change-log report from Jira'
dependsOn project.tasks.releaseChecks
doFirst {
logger.lifecycle( "Appending version `${project.releaseVersion}` to changelog..." )
ChangeLogFile.update( ormVersion.fullName );
}
}
task changeToReleaseVersion {
group 'Release'
description 'Updates `gradle/version.properties` file to the specified release-version'
dependsOn project.tasks.releaseChecks
doFirst {
logger.lifecycle( "Updating version-file to release-version : `${project.releaseVersion}`" )
updateVersionFile( project.releaseVersion )
}
}
task gitPreparationForRelease {
dependsOn changeLogFile
dependsOn changeToReleaseVersion
doLast {
logger.lifecycle( "Performing pre-steps Git commit : `${project.releaseVersion}`" )
executeGitCommand( 'add', '.' )
executeGitCommand( 'commit', '-m', "Pre-steps for release : `${project.ormVersion.fullName}`" )
}
}
task changeToDevelopmentVersion {
group 'Release'
description 'Updates `gradle/version.properties` file to the specified development-version'
dependsOn project.tasks.releaseChecks
doFirst {
logger.lifecycle( "Updating version-file to development-version : `${project.developmentVersion}`" )
updateVersionFile( project.developmentVersion )
}
}
task gitTasksAfterRelease {
dependsOn changeToDevelopmentVersion
doLast {
logger.lifecycle( "Performing pre-steps Git commit : `${project.releaseVersion}`" )
executeGitCommand( 'add', '.' )
executeGitCommand( 'commit', '-m', "Post-steps for release : `${project.ormVersion.fullName}`" )
if ( project.createTag ) {
logger.lifecycle("Tagging release : `${project.releaseTag}`...")
executeGitCommand( 'tag', project.releaseTag )
}
}
}
void updateVersionFile(String version) {
logger.lifecycle( "Updating `gradle/version.properties` version to `${version}`" )
project.ormVersionFile.text = "hibernateVersion=${version}"
}
task publishReleaseArtifacts {
dependsOn releaseChecks
dependsOn uploadDocumentation
dependsOn uploadBundlesSourceForge
mustRunAfter gitPreparationForRelease
}
task release {
group 'Release'
description 'Performs a release on local check-out, including updating changelog and '
dependsOn gitPreparationForRelease
dependsOn publishReleaseArtifacts
finalizedBy gitTasksAfterRelease
}
rootProject.subprojects.each { Project subProject ->
if ( project.name != subProject.name ) {
if ( subProject.tasks.findByName( 'release' ) ) {
project.tasks.publishReleaseArtifacts.dependsOn( subProject.tasks.release )
subProject.tasks.release.dependsOn( releaseChecks )
subProject.tasks.release.mustRunAfter( releaseChecks )
}
}
}
task ciReleaseChecks {
dependsOn releaseChecks
}
task gitTasksAfterCiRelease {
dependsOn gitTasksAfterRelease
doLast {
if ( project.createTag ) {
logger.lifecycle( "Pushing branch and tag to remote `${project.gitRemote}`..." )
executeGitCommand( 'push', '--atomic', project.gitRemote , project.gitBranch, project.releaseTag )
}
else {
logger.lifecycle("Pushing branch to remote `${project.gitRemote}`..." )
executeGitCommand( 'push', project.gitRemote , project.gitBranch )
}
}
}
task ciRelease {
group 'Release'
description 'Performs a release: the hibernate version is set and the changelog.txt file updated, the changes are pushed to github, then the release is performed, tagged and the hibernate version is set to the development one.'
dependsOn ciReleaseChecks
dependsOn release
finalizedBy gitTasksAfterCiRelease
}
static String executeGitCommand(Object ... subcommand){
List<Object> command = ['git']
Collections.addAll( command, subcommand )
def proc = command.execute()
def code = proc.waitFor()
def stdout = inputStreamToString( proc.getInputStream() )
def stderr = inputStreamToString( proc.getErrorStream() )
if ( code != 0 ) {
throw new GradleException( "An error occurred while executing " + command + "\n\nstdout:\n" + stdout + "\n\nstderr:\n" + stderr )
}
return stdout
}
static String inputStreamToString(InputStream inputStream) {
inputStream.withCloseable { ins ->
new BufferedInputStream(ins).withCloseable { bis ->
new ByteArrayOutputStream().withCloseable { buf ->
int result = bis.read();
while (result != -1) {
buf.write((byte) result);
result = bis.read();
}
return buf.toString( StandardCharsets.UTF_8.name());
}
}
}
}
class ChangeLogFile {
// Get the Release Notes from Jira and add them to the Hibernate changelog.txt file
static void update(String releaseVersion) {
def text = ""
File changelog = new File( "changelog.txt" )
def newReleaseNoteBlock = getNewReleaseNoteBlock(releaseVersion)
changelog.eachLine {
line ->
if ( line.startsWith( "Note:" ) ) {
text += line + System.lineSeparator() + System.lineSeparator() + newReleaseNoteBlock
}
else {
text += line + System.lineSeparator()
}
}
changelog.text = text
}
// Get the Release Notes from Jira
static String getNewReleaseNoteBlock(String releaseVersion) {
def restReleaseVersion;
if ( releaseVersion.endsWith( ".Final" ) ) {
restReleaseVersion = releaseVersion.replace( ".Final", "" )
}
else {
restReleaseVersion = releaseVersion
}
def apiString = "https://hibernate.atlassian.net/rest/api/2/search/?jql=project=HHH%20AND%20fixVersion=${restReleaseVersion}%20order%20by%20issuetype%20ASC"
def apiUrl = new URL( apiString )
def jsonReleaseNotes = new JsonSlurper().parse( apiUrl )
def releaseDate = new Date().format( 'MMMM dd, YYYY' )
def versionId = getVersionId( jsonReleaseNotes, restReleaseVersion )
ReleaseNote releaseNotes = new ReleaseNote( releaseVersion, releaseDate, versionId )
def issuetype
jsonReleaseNotes.issues.each {
issue ->
if ( issuetype != issue.fields.issuetype.name ) {
issuetype = issue.fields.issuetype.name
releaseNotes.addEmptyLine();
releaseNotes.addLine( "** ${issue.fields.issuetype.name}" )
}
releaseNotes.addLine( " * [" + issue.key + "] - " + issue.fields.summary )
}
releaseNotes.addEmptyLine()
return releaseNotes.notes
}
private static getVersionId(jsonReleaseNotes, String restReleaseVersion) {
def fixVersions = jsonReleaseNotes.issues.get( 0 ).fields.fixVersions
for ( def fixVersion : fixVersions ) {
if ( fixVersion.name.equals( restReleaseVersion ) ) {
return fixVersion.id
}
}
throw new GradleException( "Unable to determine the version id of the current release." )
}
}
class ReleaseNote {
String notes;
String notesHeaderSeparator = "------------------------------------------------------------------------------------------------------------------------"
ReleaseNote(String releaseVersion, String releaseDate, String versionId) {
notes = "Changes in ${releaseVersion} (${releaseDate})" + System.lineSeparator()
addHeaderSeparator()
addEmptyLine()
addLine( "https://hibernate.atlassian.net/projects/HHH/versions/${versionId}" )
}
void addLine(String text) {
notes += text + System.lineSeparator()
}
void addHeaderSeparator() {
addLine( notesHeaderSeparator )
}
void addEmptyLine() {
notes += System.lineSeparator()
}
void addEmptyLines(int numberOfLines) {
for ( i in 1..numberOfLines ) {
notes += System.lineSeparator()
}
}
}
gradle.getTaskGraph().whenReady {tg->
if ( tg.hasTask( project.tasks.releaseChecks ) ) {
String releaseVersionLocal
String developmentVersionLocal
def console = tg.hasTask( project.tasks.ciReleaseChecks )
? null
: System.console()
if (project.hasProperty('releaseVersion')) {
releaseVersionLocal = project.property('releaseVersion')
}
else {
if (console) {
// prompt for `releaseVersion`
releaseVersionLocal = console.readLine('> Enter the release version: ')
}
else {
throw new GradleException(
"`release`-related tasks require the following properties: 'releaseVersion', 'developmentVersion'"
)
}
}
if (project.hasProperty('developmentVersion')) {
developmentVersionLocal = project.property('developmentVersion')
}
else {
if (console) {
// prompt for `developmentVersion`
developmentVersionLocal = console.readLine('> Enter the next development version: ')
}
else {
throw new GradleException(
"`release`-related tasks require the following properties: 'releaseVersion', 'developmentVersion'"
)
}
}
assert releaseVersionLocal != null && developmentVersionLocal != null;
// set up information for the release-related tasks
project.ext {
releaseVersion = releaseVersionLocal;
developmentVersion = developmentVersionLocal;
createTag = !project.hasProperty('noTag')
releaseTag = project.createTag ? determineReleaseTag(releaseVersionLocal) : ''
}
logger.lifecycle("Checking that the working tree is clean...")
String uncommittedFiles = executeGitCommand('status', '--porcelain')
if (!uncommittedFiles.isEmpty()) {
throw new GradleException(
"Cannot release because there are uncommitted or untracked files in the working tree.\n" +
"Commit or stash your changes first.\n" +
"Uncommitted files:\n " +
uncommittedFiles
);
}
if (tg.hasTask(project.tasks.ciReleaseChecks)) {
String gitBranchLocal
String gitRemoteLocal
if (project.hasProperty('gitBranch')) {
gitBranchLocal = project.property('gitBranch')
}
else {
gitBranchLocal = executeGitCommand( 'branch', '--show-current' )
}
if (project.hasProperty('gitRemote')) {
gitRemoteLocal = project.property('gitRemote')
}
else {
final String remotes = executeGitCommand( 'remote', 'show' )
final List<String> tokens = remotes.tokenize()
if ( tokens.size() != 1 ) {
throw new GradleException( "Could not determine `gitRemote` property for `ciRelease` tasks." )
}
gitRemoteLocal = tokens.get( 0 )
}
project.ext {
gitBranch = gitBranchLocal
gitRemote = gitRemoteLocal
}
logger.lifecycle("Switching to branch '${project.gitBranch}'...")
executeGitCommand('checkout', project.gitBranch)
logger.lifecycle("Checking that all commits are pushed...")
String diffWithUpstream = executeGitCommand('diff', '@{u}')
if (!diffWithUpstream.isEmpty()) {
throw new GradleException(
"Cannot perform `ciRelease` tasks because there are un-pushed local commits .\n" +
"Push your commits first."
);
}
}
}
}
static String determineReleaseTag(String releaseVersion) {
return releaseVersion.endsWith( '.Final' )
? releaseVersion.replace( ".Final", "" )
: releaseVersion;
}