480 lines
15 KiB
Groovy
480 lines
15 KiB
Groovy
plugins {
|
|
id 'java'
|
|
id 'war'
|
|
id 'jacoco'
|
|
id 'org.sonarqube' version '3.5.0.2730'
|
|
id 'com.github.node-gradle.node' version '3.5.1'
|
|
}
|
|
|
|
group = 'ru.akarpov.web4'
|
|
version = '1.0-SNAPSHOT'
|
|
sourceCompatibility = '17'
|
|
|
|
// Load all properties from gradle.properties
|
|
def props = new Properties()
|
|
file("${projectDir}/gradle.properties").withInputStream { props.load(it) }
|
|
|
|
repositories {
|
|
mavenCentral()
|
|
}
|
|
|
|
dependencies {
|
|
// Jakarta EE
|
|
providedCompile "jakarta.platform:jakarta.jakartaee-api:${props.jakartaEeVersion}"
|
|
|
|
// Hibernate
|
|
implementation "org.hibernate.orm:hibernate-core:${props.hibernateVersion}"
|
|
|
|
// PostgreSQL
|
|
implementation "org.postgresql:postgresql:${props.postgresqlVersion}"
|
|
|
|
// JWT for authentication
|
|
implementation "com.auth0:java-jwt:${props.jwtVersion}"
|
|
|
|
// JSON-B
|
|
implementation "jakarta.json.bind:jakarta.json.bind-api:${props.jsonbVersion}"
|
|
|
|
// Swagger/OpenAPI
|
|
providedCompile "org.eclipse.microprofile.openapi:microprofile-openapi-api:${props.openapiVersion}"
|
|
implementation "io.swagger.core.v3:swagger-annotations:${props.swaggerVersion}"
|
|
implementation "io.swagger.core.v3:swagger-jaxrs2-jakarta:${props.swaggerVersion}"
|
|
implementation "org.webjars:swagger-ui:${props.swaggeruiVersion}"
|
|
|
|
// Testing
|
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${props.junitVersion}"
|
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${props.junitVersion}"
|
|
testImplementation "org.mockito:mockito-core:${props.mockitoVersion}"
|
|
testImplementation "org.mockito:mockito-junit-jupiter:${props.mockitoVersion}"
|
|
|
|
// Selenium for functional testing
|
|
testImplementation "org.seleniumhq.selenium:selenium-java:${props.seleniumVersion}"
|
|
testImplementation "io.github.bonigarcia:webdrivermanager:${props.webdriverManagerVersion}"
|
|
}
|
|
|
|
// Define the main Java source directory
|
|
sourceSets {
|
|
main {
|
|
java {
|
|
srcDirs = ['src/main/java']
|
|
}
|
|
resources {
|
|
srcDirs = ['src/main/resources']
|
|
}
|
|
}
|
|
test {
|
|
java {
|
|
srcDirs = ['src/test/java']
|
|
}
|
|
resources {
|
|
srcDirs = ['src/test/resources']
|
|
}
|
|
}
|
|
}
|
|
|
|
// Configure Node plugin for frontend build
|
|
node {
|
|
version = "${props.nodeVersion}"
|
|
npmVersion = "${props.npmVersion}"
|
|
download = true
|
|
nodeProjectDir = file("${projectDir}/frontend")
|
|
}
|
|
|
|
// Task 1: compile - Compile Java sources
|
|
task compile(type: JavaCompile) {
|
|
source = sourceSets.main.java.srcDirs
|
|
classpath = sourceSets.main.compileClasspath
|
|
destinationDir = sourceSets.main.java.outputDir
|
|
options.encoding = 'UTF-8'
|
|
}
|
|
|
|
// Task 2: build - Build the entire project
|
|
task buildProject(dependsOn: [compile, war, npmBuild]) {
|
|
description = 'Builds the entire project (backend and frontend)'
|
|
group = 'build'
|
|
}
|
|
|
|
// NPM tasks for frontend
|
|
task npmInstall(type: NpmTask) {
|
|
description = 'Installs all dependencies from package.json'
|
|
workingDir = file("${projectDir}/frontend")
|
|
args = ['install']
|
|
}
|
|
|
|
task npmBuild(type: NpmTask, dependsOn: npmInstall) {
|
|
description = 'Builds the frontend'
|
|
workingDir = file("${projectDir}/frontend")
|
|
args = ['run', 'build']
|
|
}
|
|
|
|
// Task to copy frontend build to backend webapp directory
|
|
task copyFrontendToBackend(type: Copy, dependsOn: npmBuild) {
|
|
from "${projectDir}/frontend/build"
|
|
into "${buildDir}/webapp"
|
|
|
|
// Preserve swagger-ui.html
|
|
doFirst {
|
|
mkdir "${buildDir}/temp"
|
|
if (file("${projectDir}/src/main/webapp/swagger-ui.html").exists()) {
|
|
copy {
|
|
from "${projectDir}/src/main/webapp/swagger-ui.html"
|
|
into "${buildDir}/temp"
|
|
}
|
|
}
|
|
}
|
|
|
|
doLast {
|
|
if (file("${buildDir}/temp/swagger-ui.html").exists()) {
|
|
copy {
|
|
from "${buildDir}/temp/swagger-ui.html"
|
|
into "${buildDir}/webapp"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 3: clean - Clean build directories
|
|
clean {
|
|
description = 'Cleans all build directories and temporary files'
|
|
delete "${buildDir}"
|
|
delete "${projectDir}/frontend/build"
|
|
delete "${projectDir}/frontend/node_modules"
|
|
delete fileTree(dir: "${projectDir}/src/main/webapp", excludes: ["WEB-INF/**"])
|
|
}
|
|
|
|
// Configure war task to include frontend
|
|
war {
|
|
dependsOn copyFrontendToBackend
|
|
webAppDirectory = file("${buildDir}/webapp")
|
|
archiveName = "web-lab4.war"
|
|
manifest {
|
|
attributes(
|
|
'Implementation-Title': project.name,
|
|
'Implementation-Version': project.version,
|
|
'Main-Class': "${props.mainClass}"
|
|
)
|
|
}
|
|
}
|
|
|
|
// Task 4: test - Run JUnit tests
|
|
test {
|
|
useJUnitPlatform()
|
|
finalizedBy jacocoTestReport
|
|
|
|
// Exclude functional tests from regular test task
|
|
exclude '**/functional/**'
|
|
}
|
|
|
|
// Task 5: scp - Transfer built project to a server
|
|
task scp(dependsOn: buildProject) {
|
|
description = 'Transfers the built project to a remote server'
|
|
doLast {
|
|
exec {
|
|
commandLine 'scp', "${buildDir}/libs/web-lab4.war", "${props.remoteUser}@${props.remoteHost}:${props.remotePath}"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 6: xml - Validate all XML files
|
|
task xml {
|
|
description = 'Validates all XML files in the project'
|
|
doLast {
|
|
fileTree(dir: projectDir, include: '**/*.xml').each { file ->
|
|
javaexec {
|
|
classpath = configurations.runtimeClasspath
|
|
main = 'javax.xml.validation.SchemaFactory'
|
|
args = [file.absolutePath]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 7: doc - Add MD5 and SHA-1 to MANIFEST and generate JavaDoc
|
|
task doc(dependsOn: war) {
|
|
description = 'Adds MD5 and SHA-1 checksums to MANIFEST and generates JavaDoc'
|
|
doLast {
|
|
// Generate JavaDoc
|
|
exec {
|
|
workingDir projectDir
|
|
commandLine 'javadoc', '-d', "${buildDir}/docs/javadoc",
|
|
'-sourcepath', "${projectDir}/src/main/java",
|
|
'-subpackages', 'ru.akarpov.web4'
|
|
}
|
|
|
|
// Calculate checksums and add to MANIFEST
|
|
def warFile = file("${buildDir}/libs/web-lab4.war")
|
|
def md5 = warFile.digest('MD5')
|
|
def sha1 = warFile.digest('SHA-1')
|
|
|
|
war.manifest {
|
|
attributes(
|
|
'MD5-Checksum': md5,
|
|
'SHA1-Checksum': sha1
|
|
)
|
|
}
|
|
|
|
// Rebuild the WAR file with updated MANIFEST
|
|
war.execute()
|
|
|
|
// Add JavaDoc to the WAR file
|
|
ant.zip(destfile: warFile, update: true) {
|
|
zipfileset(dir: "${buildDir}/docs/javadoc", prefix: 'javadoc')
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 8: native2ascii - Convert localization files
|
|
task native2ascii {
|
|
description = 'Converts localization files using native2ascii'
|
|
doLast {
|
|
fileTree(dir: "${projectDir}/src/main/resources", include: '**/*.properties').each { file ->
|
|
def outputFile = new File(file.absolutePath + '.ascii')
|
|
exec {
|
|
commandLine 'native2ascii', '-encoding', 'UTF-8', file.absolutePath, outputFile.absolutePath
|
|
}
|
|
outputFile.renameTo(file)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 9: music - Play music after build
|
|
task music(dependsOn: buildProject) {
|
|
description = 'Plays music after successful build'
|
|
doLast {
|
|
if (System.properties['os.name'].toLowerCase().contains('windows')) {
|
|
exec {
|
|
commandLine 'cmd', '/c', "start ${props.musicFile}"
|
|
}
|
|
} else if (System.properties['os.name'].toLowerCase().contains('mac')) {
|
|
exec {
|
|
commandLine 'afplay', "${props.musicFile}"
|
|
}
|
|
} else {
|
|
exec {
|
|
commandLine 'xdg-open', "${props.musicFile}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 10: report - Save JUnit report to XML and commit to Git
|
|
task report(dependsOn: test) {
|
|
description = 'Saves JUnit report to XML and commits to Git'
|
|
doLast {
|
|
if (!project.gradle.taskGraph.hasTask(':test') || tasks.test.state.failure == null) {
|
|
def reportFile = file("${buildDir}/test-results/test/TEST-junit-report.xml")
|
|
exec {
|
|
commandLine 'git', 'add', reportFile.absolutePath
|
|
}
|
|
exec {
|
|
commandLine 'git', 'commit', '-m', 'Add JUnit test report'
|
|
}
|
|
println "Test report committed to Git: ${reportFile.absolutePath}"
|
|
} else {
|
|
println "Tests failed, report not committed"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 11: diff - Check working copy and commit if specified classes are not changed
|
|
task diff {
|
|
description = 'Checks working copy and commits if specified classes are not changed'
|
|
doLast {
|
|
def changedFiles = ""
|
|
exec {
|
|
commandLine 'git', 'status', '--porcelain'
|
|
standardOutput = new ByteArrayOutputStream()
|
|
changedFiles = standardOutput.toString().trim()
|
|
}
|
|
|
|
def excludedClasses = props.excludedClasses.split(',')
|
|
boolean canCommit = true
|
|
|
|
for (String changedFile : changedFiles.split('\n')) {
|
|
def fileName = changedFile.substring(3)
|
|
for (String excludedClass : excludedClasses) {
|
|
if (fileName.contains(excludedClass)) {
|
|
canCommit = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if (canCommit && !changedFiles.isEmpty()) {
|
|
exec {
|
|
commandLine 'git', 'add', '.'
|
|
}
|
|
exec {
|
|
commandLine 'git', 'commit', '-m', 'Auto-commit: Changes not affecting excluded classes'
|
|
}
|
|
println "Changes committed to Git"
|
|
} else {
|
|
println "Changes affect excluded classes or no changes found, not committing"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 12: team - Get previous revisions, build them and package as zip
|
|
task team {
|
|
description = 'Gets previous revisions, builds them and packages as zip'
|
|
doLast {
|
|
def currentDir = file("${buildDir}/team")
|
|
currentDir.mkdirs()
|
|
|
|
// Get the last 2 revisions
|
|
for (int i = 1; i <= 2; i++) {
|
|
def revisionDir = new File(currentDir, "revision-${i}")
|
|
revisionDir.mkdirs()
|
|
|
|
exec {
|
|
commandLine 'git', 'archive', "HEAD~${i}", '--format=tar',
|
|
'--output', "${revisionDir}/archive.tar"
|
|
}
|
|
|
|
exec {
|
|
workingDir revisionDir
|
|
commandLine 'tar', '-xf', 'archive.tar'
|
|
}
|
|
|
|
// Build the revision
|
|
exec {
|
|
workingDir revisionDir
|
|
commandLine './gradlew', 'build'
|
|
}
|
|
}
|
|
|
|
// Package the builds into a zip file
|
|
ant.zip(destfile: "${buildDir}/team-builds.zip") {
|
|
fileset(dir: "${buildDir}/team")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 13: alt - Create alternative version with renamed variables and classes
|
|
task alt {
|
|
description = 'Creates alternative version with renamed variables and classes'
|
|
doLast {
|
|
def altDir = file("${buildDir}/alt")
|
|
copy {
|
|
from "${projectDir}/src"
|
|
into "${altDir}/src"
|
|
}
|
|
|
|
def replacements = [
|
|
'User': 'AppUser',
|
|
'Point': 'Coordinate',
|
|
'PointService': 'CoordinateService',
|
|
'UserService': 'AppUserService'
|
|
]
|
|
|
|
fileTree(dir: altDir, include: '**/*.java').each { file ->
|
|
def content = file.text
|
|
replacements.each { key, value ->
|
|
content = content.replaceAll(key, value)
|
|
}
|
|
file.text = content
|
|
}
|
|
|
|
// Build the alternative version
|
|
exec {
|
|
workingDir altDir
|
|
commandLine './gradlew', 'build'
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 14: history - Try to compile, if fails, get previous version from Git
|
|
task history {
|
|
description = 'If compilation fails, gets previous version from Git until a working one is found'
|
|
doLast {
|
|
boolean compileSuccess = false
|
|
int revision = 0
|
|
|
|
while (!compileSuccess && revision < 10) { // Limit to last 10 revisions
|
|
try {
|
|
if (revision > 0) {
|
|
// Checkout previous revision
|
|
exec {
|
|
commandLine 'git', 'checkout', "HEAD~${revision}"
|
|
}
|
|
}
|
|
|
|
// Try to compile
|
|
tasks.compile.execute()
|
|
compileSuccess = true
|
|
|
|
if (revision > 0) {
|
|
// Create diff file with the next revision (first broken one)
|
|
exec {
|
|
standardOutput = new FileOutputStream("${buildDir}/first-broken-diff.patch")
|
|
commandLine 'git', 'diff', "HEAD..HEAD~${revision-1}"
|
|
}
|
|
println "Found working revision at HEAD~${revision}, diff saved to first-broken-diff.patch"
|
|
}
|
|
} catch (Exception e) {
|
|
println "Compilation failed for revision HEAD~${revision}, trying older version"
|
|
revision++
|
|
}
|
|
}
|
|
|
|
// Restore to original revision
|
|
if (revision > 0) {
|
|
exec {
|
|
commandLine 'git', 'checkout', 'HEAD'
|
|
}
|
|
}
|
|
|
|
if (!compileSuccess) {
|
|
println "Could not find a working revision in the last 10 commits"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task 15: env - Build and run in alternative environments
|
|
task env {
|
|
description = 'Builds and runs the program in alternative environments'
|
|
doLast {
|
|
def environments = props.environments.split(';')
|
|
|
|
environments.each { env ->
|
|
def (javaVersion, vmArgs) = env.split(':')
|
|
println "Building and running with Java ${javaVersion} and VM args ${vmArgs}"
|
|
|
|
exec {
|
|
environment 'JAVA_HOME', "${props.javaHome}${javaVersion}"
|
|
commandLine './gradlew', 'build'
|
|
}
|
|
|
|
exec {
|
|
environment 'JAVA_HOME', "${props.javaHome}${javaVersion}"
|
|
commandLine "${props.javaHome}${javaVersion}/bin/java",
|
|
vmArgs.split(',') as List,
|
|
'-jar',
|
|
"${buildDir}/libs/web-lab4.war"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task for functional testing
|
|
task functionalTest(type: Test) {
|
|
description = 'Runs functional tests'
|
|
group = 'verification'
|
|
|
|
testClassesDirs = sourceSets.test.output.classesDirs
|
|
classpath = sourceSets.test.runtimeClasspath
|
|
|
|
// Only run tests in the functional package
|
|
include '**/functional/**'
|
|
|
|
// Set system property to identify functional tests
|
|
systemProperty 'test.type', 'functional'
|
|
|
|
// Report results
|
|
reports {
|
|
html.enabled = true
|
|
junitXml.enabled = true
|
|
}
|
|
}
|
|
|
|
// Integrate functional testing into the build process
|
|
build.dependsOn functionalTest |