martes, 2 de agosto de 2022

Gradle 7 falta docker, jacoco, quality ...

Gradle-7 (14) Gradle Draft(2). Multiproject

 1. Defining layers

Common layers are:
1. User Interface (UI) that can contain HTML, javascript, and CSS files
2. API or server-side application responding HTTP request (java)
3. Database connection (DB layer)  consumed by the API layer (java)

2. Create a Multiproject 

Eclipse:
"File-> New -> Gradle -> Gradle Project"
Select java version 15 or below, and maven 7.5

Gradle:
gradle init
Type of project: 2. Application
Language: 3. Java
Split functionality: 1. no
Script DSL: 1.Groovy
Test framework: 4. JUnit Jupiter
Project name: (the name of the folder)
Source package: (any name)

settings.gradle file content:
rootProjectName= 'your-project-name'
include ('app') // in gradle or include ('lib') in Eclipse  creation

Show projects:
./gradlew projects

Create a subproject
-Add the new project reference in settings.gradle
       include ('app', 'newProject')
   or without parentheses
       include 'app', 'newProject'
   or separating lines
   include 'app'
   include 'newProject' 
-Add a subfolder (newProject) that will contain the project and add the build.gradle and the src/main/java and other folder needed by a standard java project

-Adding a dependency on another project
dependency {
    implementation project(':app')
}

3. The copy and zip tasks

NOTE: Gradle uses routes relative to the directory where the build.path is 

1. Copying one directory:
tasks.register('copyDescription', Copy) {
    from 'descriptions//directory 
         into "$buildDir/descriptions"
}

2. Copying one file:
tasks.register('copyDescription', Copy) {
    from file 'descriptions/myFile.txt
         into "$buildDir/descriptions"
}

3. Copying one file into a directory and create it:
tasks.register('copyDescription', Copy) {
    from file 'descriptions/myFile.txt
         into layout.buildDirectory.dir('descriptions')
}

4. Copying multiple files with  pattern:
tasks.register('copyDescription', Copy) {
    from fileTree(layout.projectDirectory)
    include 'descriptions/**
         into "$buildDir/descriptions"
}

5. Copying multiple files with  pattern but REMOVING directory structure:
tasks.register('copyDescription', Copy) {
    from fileTree(layout.projectDirectory) {
        include 'description/**
    }.files
         into layout.buildDirectory.dir('descriptions')
}

5. Copying multiple files to ZIP
tasks.register('copyDescription', Copy) {
    from 'descriptions
     into "$buildDir/descriptions"
}
tasks.register('zipDescription', Zip) {
    from copyDescription   // files resulting from copyDescription task
    destinationDirectory buildDir
    from 'descriptions.zip
}

6. Copying multiple files to ZIP to another directory
tasks.register('zipDescription', Zip) {
    from copyDescription   // files resulting from copyDescription task
    destinationDirectory layout.buildDirectory.dir('myZipDirectory')
    // or destinationDirectory = file ("$buildDir/myZipDirectory")
    from 'descriptions.zip
}

4. "buildScr" and convention plugins

buildSrc applies the "build.gradle" logic to multiple projects, using convention plugins (it is similar to "include" in C). Eclipse can be very tricky and maybe gets into errors

Create a project with Gradle:
gradle init
Type of project: 2. Application
Language: 3. Java
Split functionality: 2. yes
Script DSL: 1.Groovy
Test framework: 4. JUnit Jupiter
Project name: (the name of the folder)
Source package: (any name)

The project structure is:

The build.gradle in the buildSrc folder is:
plugins {
    // Support convention plugins written in Groovy. Convention plugins are build scripts
         // in 'src/main' that automatically become available as plugins in the main build.
    id 'groovy-gradle-plugin'
}
repositories {
    // Use the plugin portal to apply community plugins in convention plugins.
    gradlePluginPortal()
}

And the ·.gradle" files in the "buildScr/src/main/groovy" are:

lesson08.java-common-conventions.gradle:
plugins {
    id 'java'
}
repositories {
    mavenCentral()
}
dependencies {
    constraints {
        implementation 'org.apache.commons:commons-text:1.9'
    }
   testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
}
tasks.named('test') {
    useJUnitPlatform()
}

lesson08.java-library-conventions.gradle:
plugins {
    // Apply the common convention plugin for shared build configuration between 
         // library and application projects.
    id 'lesson08.java-common-conventions'

    // Apply the java-library plugin for API and implementation separation.
    id 'java-library'
}

lesson08.java-application-conventions.gradle:
plugins {
    // Apply the common convention plugin for shared build configuration between 
         // library and application projects.
    id 'lesson08.java-common-conventions'

    // Apply the aplication plugin to add support for building a CLI application in Java
    id 'application'
}

5. Create custon tags

These are the steps:
1. Can define tasks class in build.gradle
2. must extend DefaultTask
3. add a @TaskAction method
4. extract configuration properties into methods
5. return Property<T> (or class that extends this)
6. have @Input type annotation
7. move task class to buildSrc for reuse

5.1 Create the task FileDiffTask in the build.gradle file

In this example, it is compared the size of a file given by param with a fixed file:
This code is added to the build.gradle:
//Define the type of the action
abstract class FileDiffTask extends DefaultTask {
  @InputFile  //Define input parameter of type file
  abstract RegularFileProperty getFile1() //Define property file1

  @TaskAction //Define the action of the task
  def diff() {
    //Fixed file
    File file2 = project.file('src/main/resources/static/images/logflume.jpg')
    if (file1.get().asFile.size() == file2.size()) {
      println "${file1.get().asFile.name} and {file2.name} have the same size"
    }else if (file1.size() > file2.size()) {
      println "${file1.get().asFile.name} was larger"
    }else {
        println "{file2.name} was larger"
    }
  }
}
//Create an instance of the task
task.register('fileDiff', FileDiffTask) {
    file1 = file('src/main/resources/static/images/rollercoaster.jpg')
}

5.2 Inserting the logic in the buildSrc folder

1. Create a package in the buildSrc/src/main/groovy folder (e.g. "com.ximo")
2. Add a Groovy class with the same name as the task (FileDiffTask)
3. Add the import declaration at the top of the file of this Groovy class
import org.gradle.api.tasks.TaskAction

3. Copy the previous code of the definition of the class (not the "tasks.register" part )

4. In the build.gradle of the project (not the buildSrc) file remove the definition of the class (as now it is copied to the Groovy class in the buildSrc folder) and add the import declaration
import com.ximo.FileDiffTask

5. Now the file build.gradle is as follows
import com.ximo.FileDiffTask

plugins {
    id 'java-library'
}
//Create an instance of the task
task.register('fileDiff', FileDiffTask) {
    file1 = file('src/main/resources/static/images/rollercoaster.jpg')
}

6. Now execute the task as ./gradlew fileDiff

6. Create custom plugin

These are the steps:
1. A plugin can contain tasks
2. implements "Plugin" interface
3. use "apply pluggin: <name> " for "inline" plugins
4. use "extensions" for configuring the plugin
5.  create "extension class" and a "named instance".

6.1 Create the plugin FileDiffPlugin in the build.gradle file

import com.ximo.FileDiffTask //References the task to assign  to the plugin

plugins {
    id 'java-library'
}

//Define the extension the plugin with the parameter to use (file1)
abstract class FileDiffPluginExtension {
    abstract Regular Property getFile1() //parameter "file1" 
}

// Define the value of  parameter "file1"
fileDiff {
    file1 = file('src/main/resources/static/images/rollercoaster.jpg')
}

//Define the class of the plugin
class FileDiffPlugin implements Plugin<Project> {
  @Override
  void apply(Project project) {
    //Adds the extension
    def extension = project.extensions.create('fileDiff', FileDiffPluginExtension)
          //Adds the task with the parameter
    project.tasks.register('fileDiff', FileDiffTask) {
      file1 = extension.file1 //indicated in fileDiff
    }
  }
}
// Apply the plugin
apply plugin: FileFiffPlugin

Now execute the task as ./gradlew fileDiff

6.2 Inserting the logic in Maven

Note that the plugin consists of 3 classes (Plugin class itself, Extensions and  Tasks classes)
1. Create a gradle project (create folder ->gradle init->1.basic -> 1.Groovy -> default no -> default)
2. In the build.gradle:
plugins {
    id 'groovy-gradle-plugin' // the same as convention plugins
    id 'maven-publish'  // for publishing in maven
}

// for publishing in gradle we need a group and version
group = 'com.ximo'
version = '0.1.0-SNAPSHOT''

gradlePlugin {
  plugins {
    fileDiff {
      id = 'com.ximo.file-diff// unique name
      implementationClass 'com.gradlehero.FileDiffPlugin'
    }
  }

3. Create folder src/main/groovy/com/ximo
4. Create groovy classes for plugins, extensions and tasks

5. FileDiffPlugin.groovy (plugin class)
package com.ximo
import org.gradle.api.Plugin
import org.gradle.api.Project
//Define the class of the plugin
class FileDiffPlugin implements Plugin<Project> {
  @Override
  void apply(Project project) {
    //Adds the extension
    def extension = project.extensions.create('fileDiff', FileDiffPluginExtension)
          //Adds the task with the parameter
    project.tasks.register('fileDiff'FileDiffTask) {
      file1 = extension.file1 //indicated in fileDiff
    }
  }
}

6. FileDiffTask.groovy (extension class)
package com.ximo
import org.gradle.api.Plugin
//Define the extension the plugin with the parameter to use (file1)
abstract class FileDiffPluginExtension {
    abstract Regular Property getFile1() //parameter "file1" 
}

7. FileDiffTask.groovy (task class)
package com.ximo
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.InputFile
import org.gradle.api.TaskAction
//Define the type of the action
abstract class FileDiffTask extends DefaultTask {
  @InputFile  //Define input parameter of type file
  abstract RegularFileProperty getFile1() //Define property file1

  @TaskAction //Define the action of the task
  def diff() {
    //Fixed file
    File file2 = project.file('src/main/resources/static/images/logflume.jpg')
    if (file1.get().asFile.size() == file2.size()) {
      println "${file1.get().asFile.name} and {file2.name} have the same size"
    }else if (file1.size() > file2.size()) {
      println "${file1.get().asFile.name} was larger"
    }else {
        println "{file2.name} was larger"
    }
  }
}

8. Now we can build ẁith  ./gradlew build
9. Now we can publish ẁith  ./gradlew publishToMavenLocal
10. To use this plugin in a project, the repository must be indicated in the settings.gradle
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenLocal()
    }
}
rootProject.name='theme-park-manager'
include 'api', 'service', 'ui'



7. Test custom plugin (Spock framework)

Use the Spock framework with an expressive Groovy test
Use "GradleTestKit" for plugin functional test
In the "given:" section, create build file
in the "when:" section use "GradleRunner" to execute build
In the "then:" section verify expectation.

This Tom Gregory link is the source code

The file build.gradle

plugins {
    id 'groovy-gradle-plugin'
    id 'maven-publish'
}

group = 'com.gradlehero'
version = '0.1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
}

test {
    useJUnitPlatform()
}

gradlePlugin {
    plugins {
        fileDiff {
            id = 'com.gradlehero.file-diff'
            implementationClass = 'com.gradlehero.FileDiffPlugin'
        }
    }
}

The file test/groovy/com/gradlehero/FileDiff/PluginFunctionalTest.groovy

package com.gradlehero

import org.gradle.testkit.runner.GradleRunner
import spock.lang.Specification
import spock.lang.TempDir
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS

class FileDiffPluginFunctionalTest extends Specification {
    @TempDir
    File testProjectDir
    File testFile1
    File testFile2
    File buildFile

    def setup() {
        testFile1 = new File(testProjectDir, 'testFile1.txt')
        testFile2 = new File(testProjectDir, 'testFile2.txt')
        buildFile = new File(testProjectDir, 'build.gradle')
        buildFile << """
            plugins {
                id 'com.gradlehero.file-diff'
            }
            
            fileDiff {
                file1 = file('${testFile1.getName()}')
                file2 = file('${testFile2.getName()}')
            }
        """
    }

    def "can diff 2 files of same length"() {
        given:
        testFile1.createNewFile()
        testFile2.createNewFile()

        when:
        def result = GradleRunner.create()
            .withProjectDir(testProjectDir)
            .withArguments('fileDiff')
            .withPluginClasspath()
            .build()

        then:
        result.output.contains('testFile1.txt and testFile2.txt have the same size')
        result.task(':fileDiff').outcome == SUCCESS
    }

    def "can diff 2 files where 1st file larger than 2nd"() {
        given:
        testFile1 << 'Some text'
        testFile2.createNewFile()

        when:
        def result = GradleRunner.create()
                .withProjectDir(testProjectDir)
                .withArguments('fileDiff')
                .withPluginClasspath()
                .build()

        then:
        result.output.contains('testFile1.txt was larger')
        result.task(':fileDiff').outcome == SUCCESS
    }

    def "can diff 2 files where 2nd file larger than 1st"() {
        given:
        testFile1.createNewFile()
        testFile2 << 'Some text'

        when:
        def result = GradleRunner.create()
                .withProjectDir(testProjectDir)
                .withArguments('fileDiff')
                .withPluginClasspath()
                .build()

        then:
        result.output.contains('testFile2.txt was larger')
        result.task(':fileDiff').outcome == SUCCESS
    }
}