domingo, 30 de octubre de 2022

Docker (12) : Retomar docker en 2022 (1). Problemas de espacio docker

 1. Instalar docker en Linux Mint

#1. Update references
sudo apt update

#2. Install docker
sudo apt install docker*

#3. Verify docker version
docker --version

#4. ONLY IF there are permission problems change "mod"
sudo chmod 666 /var/run/docker.sock

2. Descargar y ejecutar imágenes desde el "Docker Registry"

#1. Descargar una imagen desde el Docker Registry
docker pull busybox  # Recoge la imagen "busybox

#2. Listar las imágenes que hemos descargado
docker images

#3. Ejecutar una imagen (y después se detiene la ejecución) (mata el proceso). Tambien puede hacer el "pull" si no se ha desgardao aún
docker run busybox # Ejecuta la imagen busybox que previamente hemos descargado

#4. Ejecutar una imagen (busibox) pasándole un parámetro (echo "hello from busybox") y despues se mata el proceso
docker run busybox echo "hello from busybox"

#5. Mostrar los procesos en ejecución
docker ps # Si los procesos anteriores han dejado de ejecutarse, no mostrará ninguno

#6. Mostrar los procesos y su estado (tanto si se han matado o están activos)
docker ps -a

#7. Ejecutar una imagen en modo iterativo (No mata el proceso)
docker run -it busybox sh # Abre una shell (terminal) y podemos ejecutar comandos hasta que terminemos con el comando "exit"

#8. Ejecutar una imagen i despues eliminarla (opción --rm)
docker run --rm busybox echo "hello from busybox" # Se ejecuta y se elimina la imagen

#9. Descarga (Elimina) los contenedores cargados a partir de su ID 
docker rm 305297d7a235 ff0a5c3750b9 # Elimina 2 procesos. Sus ID se muestran con el comando "docker ps -a"

#10. Eliminar de una vez todos los contenedores que estan parados o no activos
docker rm $(docker ps -a -q -f status=exited)
#11. Eliminar imagenes por ID (los ID se obtienen desde "docker image")
docker rmi 305297d7a235 304694e7a432 

3. Conceptos de Docker

Image: Imagen que se suele descargar desde "Docker Registy" con "docker pull"

Container:  Se crea a partir de ejecutar una imagen con "docker run", se listan con "docker ps"

Daemon: Servicio en background (segundo plano)  de la máquina anfitrión (host) que se encarga de "building, running y distribución " de los Containers.

Client: Herramienta de línea de comando que que permite al usuario interactuar con el daemon.

Hub: Registro de todas las images.

4. Puertos, contenedores detached (creacion y parada), Imagenes

#1. Descargar una imagen desde el Docker Registry, ejecutarla en mode interactivo (-it) y borrarla despues (--rm)
docker run --rm -it prakhar1989/static-site

#2. Descargar la imagen y separar el terminal (-d "detach") y publicar los puertos aleatoriamente (-P), y renombrar la imagen (--name)
docker run -d -P --name static-site prakhar1989/static-site

#3. Mostrar los puertos de la imagen por su nombre
docker port static-site # Y contesta 80/tcp -> 0.0.0.0:32769 y 443/tcp -> 0.0.0.0:32768
                        # Podemos abrir en el navegador la aplicación http://localhost:32769  

#4. Ejecutar la imagen y asignarle un puerto (-p)
docker run -p 8888:80 prakhar1989/static-site   # Abrimos el navegador http://localhost:8888 

#5. Parar la ejecución por ID o por nombre
docker stop static-site

Las imágenes en docker se pueden clasificar de 2 formas distintas

Base/Child: Las imagenes BASE no tienen padre (normalmente son SO). Las CHILD se instalan sobre las BASE y le aportan funcionalidad (Tomcat, Firefox..)

Official/User: OFFICIAL se mantienen y soportan desde Docker, como ubuntu, phyton ... Las USER son imagenes de usuario y su formato es usuario/nombre-imagen

5. Ejemplo de descarga de un tomcat y desplegar un fichero war

Buscamos en google "docker tomcat" y seleccionamos la entrada "tomcat- Official Image | Docker Hub", y podemos seleccionar una de estos 2 entradas (jre17-temurin-jammy o tambien 10.1.1-jdk17-temurin-jammy) donde "temurin" es el nombre de la distribución de java 17 y "jammy" es una version de Ubuntu



5.1 Prueba de carga y eliminación de la imagen (No hacer caso, solo a modo de ejemplo)

Y podemos ejecutar comprobar que existe dicha imagen, probar tomcat y luego eliminar el contenedor y la imagen 

#1. Descargar una imagen del Docker Hub con prefijo tomcat: y sufijo el de la imagen
docker pull tomcat:10.1.1-jdk17-temurin-jammy

#2. Ahora se ejecuta en modo detached y asignando el puerto 8080
docker run -d -p 8080:8080 tomcat:10.1.1-jdk17-temurin-jammy

#3. Probar el navegador http://localhost:8080 y no encuentra la página
#   Pero responde Tomcat
#4. Buscamos el container y obtenemos su ID (pr ejemplo e31e38ca842e)
docker ps -a

#5. Eliminamos el container
docker rm e31e38ca842e

#6. Eliminamos la imagen
docker rmi tomcat:10.1.1-jdk17-temurin-jammyPo

5.2 Proceso

1. Creamos una carpeta por ejemplo "docker-ximo", nos situamos en ella, copiamos el "fichero war" dentro de ella y creamos el fichero "Dockerfile" con este contenido

FROM tomcat:jdk17-temurin 
COPY mywar.war /usr/local/tomcat/webapps/ (Ver el fichero propuesto en el punto 6)

2. Creamos la imagen a partir del Dockerfile

docker build -t ximo-app .

3. Ejecutamos el contenedor de docker y averiguamos que puerto tiene y probamos en el navegador

docker run -itd  -P --name my-app-cont ximo-app

docker port my-app-cont

4. Observamos que hay problemas cuando usmos el navegador para acceder a http://localhost:49153/mywar/ ya que aunque se reconce la URL, no muestra nada pues hay recursos que pide y el host responde con 404 en cada uno. Seguramente puede que sea un tema de permisos de ficheros.

5. Al final el error procedia de la generación del WAR. La parte de usuario WEB se ha hecho en React y el fichero "paqckage.json" tenia esta referencia:

"homepage": "/W01-CSV",

Y se tendria que cambiar "/W01-CSV" por "/mywar" y volver a generar toda la parte de usuario en Visual Studio, y volver a copiar estos ficheros en la carpeta src/main/wevbapp de Eclipse

6. Modificar ficheros dentro de la imagen de Docker

Para ello actuamos tal como se indica en J->Hooq. Que adjuntamos un pequeño esquema

#1. Log into the container (by its ID) as root user (-u 0 option)
#   Note that "/bin/bash" can be changed with "/bin/sh"
docker exec -u 0 -it 4d58f2bd56b0 /bin/bash

#2. Install the nano editor
apt-get update
apt-get install nano

#3. Edit the file you want
nano myfile.txt
#4. You can use many bsh commands like ls, chmod etc ..
chmod 777 myfile.txt

NOTA: si se rearranca la imagen, hay que volver a instalar los paquetes en el contenedor. Por tanto, el fichero Dockerfile puede quedar como sigue:

FROM tomcat:jdk17-temurin 
COPY mywar.war /usr/local/tomcat/webapps/

RUN ["apt-get", "update"]
RUN ["apt-get", "-y", "install", "nano"]


7. Copiar ficheros entre contenedore docker y el ordenador físico (local)

7.1 Copiar ficheros desde el contenedor docker al ordenador físico

Los pasos son:

1. Ver el id del container con docker ps -a (por ejemplo id=d880ce60664d)

1. Entrar en el bash shell del container con 
docker exec -u 0 -it d880ce60664d /bin/bash

2. Crear un fichero (por ejemplo con ls>kk.ximo.txt) y anotar la carpeta donde hemos creado el fichero (por ejemplo /usr/local/tomcat)

3. Salir del bash shell con exit

4. Copiamos el fichero a nuestro ordenador con 
docker cp d880ce60664d:/usr/local/tomcat/kk-ximo.txt ~/fromDocker.txt

7.2 Copiar ficheros desde el ordenador físico al contenedor docker

Los pasos son:

1. Copiamos el fichero f_local.txt al contenedor nuesto ordenador con 
docker cp ~/f_local.txt d880ce60664d:/usr/local/tomcat/fromLocal.txt

8. Compartir carpetas entre el host y el contenedor docker

Creamos una carpeta por ejemplo afldr en el directorio acutal (el que se muestra la variable de entorno $PWD)  y queremos que el contenedor tenga esta carpeta mapeada a su carpeta interna  /opt/bfldr para ello ejecutamos

docker exec -u 0 -v $PWD/afldr:/opt/bfldr -it d880ce60664d /bin/bash

y podemos ejecutar dentro de la shell del contenedor: 

ls -al /opt/bfldr

dentro de la shell del contenedor y vemos que si que existe esa capeta

9. Problemas con el espacio de disco que consume docker. Cambiar la ubicacion de los ficheros de docker.NO VA BIEN!!!

NOTA: Lo bueno que tiene este proceso es que es reversible. Dejando los parámetros tal come estaba no pasa nada.

NOTA: Ir con cuidado pues parece ser que esto no funciona bien en las versiones nuevas, pues en imágenes de Oracle no funcionan bien y cuabndo rearrancamos el oprdenador se pierde la configuración!

Si tenemos problemas de espacio, podemos trasladar los ficheros de docker a otro sistema de ficheros, tal como se inidca en Wiki CasaOs y tambien en Baeldung. Para elllo podemos actuar de tres maneras:

  • Cambiando el nombre de /var/lib/docker (por ejmplo /var/lib/docker.old) Haciendo un link desde /var/lib/docker a la nueva ubicación 
  • Cambiando directamente la nueva ubicación en el fichero docker.service
  • Modificando el fichero /etc/docker/daemon.json

1. Analizar donde tenemos los ficheros de docker

Para ver el espacio consumido por docker hacemos

docker system df

y nos muestra

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          1         1         8.578GB   0B (0%)    
Containers      1         1         141.1GB   0B (0%)    
Local Volumes   1         1         33.19GB   0B (0%)    
Build Cache     0         0         0B        0B          

Podemos ver que nos esta comiendo muchio disco

Para ver donde se encuentran los ficheros docker (imagenes y contenedores):

docker info | grep Root

(o también docker info -f '{{ .DockerRootDir }}')y contesta

Docker Root Dir: /var/lib/docker

También, los volúmenes están ocupando información para mostrar donde se guardan los volúmenes, hay que ver que volúmenes disponemos con 

docker volume ls

que me contesta:

DRIVER    VOLUME NAME       
local     ximo-oracle-volume

y luego, para cad columen ejecutamos

docker volume inspect nombre_volumen

y para el volumen ximo-oracle-volumen constesta:

[                                                                        
    {                                                                    
        "CreatedAt": "2023-09-20T12:59:48+02:00",                        
        "Driver": "local",                                               
        "Labels": null,                                                  
        "Mountpoint": "/var/lib/docker/volumes/ximo-oracle-volume/_data",
        "Name": "ximo-oracle-volume",                                    
        "Options": null,                                                 
        "Scope": "local"                                                 
    }                                                                    
                                                                      


2. Detener contedores en marcha

Para ello mostramos los contenedores en marcha con:

docker ps

Y paramos cada uno de ellos con 

docker stop container_id

3. Detener el servicio de docker


#sudo systemctl stop docker.service
#sudo systemctl stop docker.socket
sudo systemctl stop docker

4. Copiar el contenido de la carpeta docker al nuevo destino 

Para copiar el contenido original de /var/lib/docker al nuevo destino

sudo rsync -aqxP /var/lib/docker/ /new/path/docker

Donde las opciones dadas son:

  • -a: permite modo archivo
  • -q: no muestra los mensajes informativos
  • -x: evita traspasar los límites de del sistema de ficheros cuando copia directorios recuisivamente
  • -P: preserva los ficheros y directorios coopiados parcialmente.

5.  Renombrar la carpeta /var/lib/docker y crearle un link a la nueva

Y cambiamos el nombre de /var/lib/docker a /var/lib/docker.old

sudo mv /var/lib/docker /var/lib/docker.old

6.  OPCION 1: Crear un link a la nueva carpeta

Creamos un link que apunte /new/path/docker desde /var/lib/docker

sudo ln -s /new/path/docker /var/lib/docker

6. OPCIÓN 2: Cambiar el fichero /lib/systemd/system/docker.service y añadir la opccion --data-root  /new/path/docker al contenido a la línea indicada

Para ello comentamos la línea anterior y escribimos una nueva

#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --data-root /media/eduard/COPIES_SEGURETAT/docker

6. OPCIÓN 3: Cambiar el fichero de configuración del Daemon /etc/docker/daemon.json

Para ello modificamos el fichero /etc/docker/daemon.json para que quede:

{                                        
   "data-root": "/new/path/docker"       
                                      

7. Rearrancar docker

sudo systemctl daemon-reload
sudo systemctl restart docker

Y comprobamos que está todo correcto

 ps aux | grep -i docker | grep -v grep

Y nos muestra para la primera opción

root     31912  1.1  0.2 1507076 74036 ?       Ssl  13:14   0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Y para la segunda opción

root     31912  1.1  0.2 1507076 74036 ?       Ssl  13:14   0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --data-root /new/path/docker


Con lo que parece que vaya bien. Podemos ver las imagenes, contenedores, arrancarlos etc.

8. Errores aparecidos

1. Tanto en la primera opción como en la segunda opción cuando ejecutamos DBeaver e intentamos conectarnos a la BD oracle, entonces nuestro contenedor se para!

2. En la opción 3 (la de cambiar el /etc/docker/daemons.json) si rearrancamos la máquina, pierde toda la información.


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
    }
}







sábado, 30 de julio de 2022

Gradle-7 (13) Gradle Draft(1). Basics

 0. Prerrequisites

1. Set the JAVA_HOME to a Java installation. Java 17 does NOT work well with gradle

1.a In the ~/.bashrc file for a single user

export JAVA_HOME=<path to your version of java>
export PATH=${PATH}:${JAVA_HOME}/bin

1.b in the /etc/profile file for all users

JAVA_HOME=<path to your version of java>
PATH=$PATH:$HOME/bin:$JAVA_HOME/bin export JAVA_HOME export PATH

1.c in the /etc/environment file 

JAVA_HOME=<path to your version of java>
export JAVA_HOME

1.d Execute  export JAVA_HOME=<path to your version of java>

2. Download gradle from download page

3. Set the PATH variable to gradle, using any of the above methods

4. Create a folder (e.g. MyProject), with a terminal go into this folder and type gradle init

5. Import the gradle project in Eclipse (File->Import->Gradle-> Existing Gradle project)

1. settings.gradle


rootProjectName=myProject
println 'Initialization Phase'

2. build.gradle

println 'Printing...'
def a = 5 ; def b=6 ; println a +b + ' Hola world ---'
println 'Configuration Phase'

description 'Just my project'
group 'com.ximodante'
version '1.0-SNAPSHOT'


3. gradlew

./gradlew tasks
./gradlew clean
./gradlew copyMessage
./gradlew cM
./gradlew --version
./gradlew wrapper --gradle-version=7.5


4. Tasks

import org.apache.tools.ant.filters.ReplaceTokens

tasks.register('copyMessage', Copy) { //Task with a class (Copy)
    group 'My Own Tasks'    // show tasks grouped when using "gradle tasks"
    description 'Copy and replace template files with substitution text'
    from 'myFile.txt'
    into "$BuildDir/myFiles"
    filter (ReplaceTokens, tokens: [TO_REPLACE: "String replacement"])
}

tasks.register('zipDescriptions',  Zip) {
    group 'My Own Tasks'
    description 'zip files'
    from "$buildDir/myFiles"  //Approach 1

    //Indicates that the output of task copyMessabe is the input (recommended)
    // and does NOT need the dependsOn 
    from copyMessage          //Approach 2       

destinationDirectory = buildDir archiveFileName = 'descriptions.zip'
    //Indicates that this task (copyMessage) must be executed before.
    //DO NOT USE IF input points to the previous task!!!!
    dependsOn task.named('copyMessage')     //Approach 1
    dependsOn copyMessage                   //Approach 2
dependsOn copyMessage, otherTask, ... //Several tasks dependences

    //Indivcates that this tasks shouild be executed after
    finalizedBy anotherTask           

    //To throw an exceptio!!!
    doLast {
        throw new GradleException ('My exception is thrown!!!!')      
    }
}

//Not recommended task definition
task('zipDescriptions2', type: Zip) { //Lower performace
    group 'My Own Tasks'
    description 'zip files'
    from "$buildDir/myFiles"
    destinationDirectory = buildDir
    archiveFileName = 'descriptions.zip'
}

//Adhoc task that is not an instance of another class
tasks.register('adhocTask') { //Task without a class
    doFirst {  //closure
       println 'hello!' 
    }
    doLast {  //closure
       println 'Bye!' 
    }
    enabled false // or true
    onlyif { // similar to enabled
       5 == 3 +2 
    }
}

//Task that uses the same name as the first task but copies to another directory
tasks.named('copyMessage') { 
    into "$BuildDir/myFiles-bis"
}

//Task that uses the same name as the first task but copies to another directory
// NOT recommended!!!
tasks.getByName('copyMessage') { 
    into "$BuildDir/myFiles-bis-bis"
}

//Call a task by its name (usually tasks added by plugin
tasks.clean { 
    doLast {
        println "Squeaky clean"
    }    
}

//Call a task by its name (usually tasks added by plugin but simpler
clean { 
    doLast {
        println "Squeaky clean"
    }    
}


4.1 Tasks documentation

https://docs.gradle.org/current/dsl/index.html, look for tasks and can see several defined tasks (Delete, Copy, jar..),  For task Delete:

tasks.register('myDelete', Delete) { //Task with a class (Copy)
    group 'My Own Tasks'    // show tasks grouped when using "gradle tasks"
    description 'Delete some stuff'
    //followSymlinks = true
}

4.2 Tasks graph

The taskinfo plugin shows us the task graph

plugins {
    id 'base'
    id "org.barfuin.gradle.taskinfo" version "1.3.0"
}

To see the tasks ->  ./gradlew build tiTree



5. Plugins

plugins {
    id 'base'
    id "org.barfuin.gradle.taskinfo" version "1.3.0" //Other plugins
}

//Alternative to "plugins" NOT RCOMMENDED
apply plugin: 'base'


// archiveFileName is defined by default in plugin id to 'gradle'
// archiveFileName is a variable that can be used in any task
// there are also other defined defined by default variables taht can be changed
base {
    archivesName = 'stuff'
}



5.1 Plugins documentation

https://plugins.gradle.org/

6. Gradle 

gradle init
1: Basic, 2: Application, 3: library, 4:Gradle plugin 
1: Groovy, 2: Kotlin

gradle wrapper


7. Git

git status
git init
git add .
git commit -m "First commit of the project"

.gitignore

8. Groovy

JVM language
Can use any standard java library
dynamic typed
less verbose (optional semicolons, run code as script, optional parenthesis)
support closures (block of code passed as a variable)
pass closures outside brackets.

9. Kotlin

JVM language
statically typed
string double quotes
has functions but NO methods
need parenthesis
closures 
pass closures outside brackets
IntelliJ suggestions of code


9.1 Example of build.gradle.kts


import org.apache.tools.ant.filters.ReplaceTokens

plugins {
    base
}

tasks.register<Copy> ("generateDescriptions") {
    from ("descriptions")
    into ("$buildDir/descriptions")
    filters(ReplaceTokens::class, "tokens" to mapOf("TO_REPLACE" to "String replacement"))
}


10. Gradle api & documentation



11. Repositories and dependencies

Maven 
Ivy
Flat directory

Built-in repositories: (Maven Central , Google Maven)

repoitories {
    mavenCentral()
    google()
    maven {
        // Custom repository
        url 'https://tomgregory-2994844798587.d.codeartifact.eu-west-1amazonaws.com/maven/demo/'
    }
}

dependencies {
    implementation ('commons-beanutls:commons-beanutls:1.9.4) {
        // Exclude transitive dependency !!!
        exclude( group = "commons-collections", module = "commons-collections" 
    }


Multiples stages to build an application (Compiling, Testing, Running)

12. Java plugin


plugins {
    id 'java'
}

Included Tasks:
1. compileJava: generates .class files in the build directory. ./gradlew compileJava .
2. processResources: copies resources into build directory. ./gradlew processResources  .
3. jar: adds compiled classes and resources to .jar archive. The name of the file is 
<project-name>-<version.jar./gradlew jar  .
4. test: compiles, processes test resources and run tests. Test report in buid directory. ./gradlew processResources  .

Type of tasks:
action task: performs an action
aggregate task: groups other tasks

Tasks to execute with ./gradlew:
clean: removes the build directory.
assemble & jar: Compile, assemble resources and build jar. (build/(classes,libs,resources/nain)
check & test: assemble task+ assemble test classes and resources and run tests (resources/test, report)
build: assemble task+ test task


Define dependencies:
Defines the configuration used to generate classpath.
Classpaths: compile and runtime 
Keywords:
    compileOnly -> compile path (e.g. "servlet-api" the implementation is supplied by the server)
    runtimeOnly -> runtime path (e.g. to connect to a DB,  you don't need to know DB internals )
    implementation -> both compile and runtime
    testCompileOnly, test implementation, testRuntime: similar for tests

Annotation processor: like Lombok or MapStruct 

Extra properties for dependencies:
For not repeating versions  use "ext" for defining a property and double quotes for using it

ext {
    jujVersion = '5.7.2' //Define version
}

dependencies {
    //Use double quotes!!
    testImplementation "org.junit.jupiter:junit-jupiter-api:$jujVersion"
    testImplementation "org.junit.jupiter:junit-jupiter-params:$jujVersion"
    testRuntimeOnly "org.junit.jupiter:junit-engine-api:$jujVersion"
} 

Project default layout:
src/main/java      >>> build/classes/java/main
src/main/resources >>> build/resources/main
src/test/java      >>> build/classes/java/test
src/test/resources >>> build/resources/test

The jar file is saved in   build/libs

Execute the application:
java -jar <jar-file-locatiom> parameter  

Selecting the main class in the jar task:

tasks.name ('jar') {
    manifest {
        attributes('Main-Class': 'org.ximo.MyMainClass')
    }
}


tasks.named('jar') {
    manifest {
        attributes('Main-Class': 'com.gradlehero.themepark.RideStatusService')
    }
}

Class types for tasks:
-  compileJava and compileTestJava are of type JavaCompile class
- processResources and processTestResources are of type Copy class
- jar is of type Jar class

Making changes in tasks (by name or by class): 
It can be made using 2 options:
- tasks.named('taskName')   That affects only to the task with "taskName"
- tasks.withType(taskType).configureEach  Affecting all the tasks of "taskType"


 //The tasks compileJava and compileTestJava use JavaCompile Class
tasks.named('compileJava') {
    options.verbose = true   //For verbose results
}
tasks.named('compileTestJava') {
    options.verbose = true   //For verbose results
}
 //The above can be simplified as
tasks.withType('JavaCompile') { options.verbose = true }

 //==========================================================
 //The tasks processResources and processTestesources use Copy Class
tasks.named('processResources') {logflume
    include '**/*.txt'  //Only copies resources that ends with ".txt"
}

 //==========================================================
 //The task jar usee Jar Class
tasks.named('jar') {
    archiveFileName = 'myJar.jar''  //Sets the jar name
}


Make sure that you have enabled tests: 
tasks.withType(Test).configureEach {
useJUnitPlatform()
}

Example of java test class:

public class RideStatusServiceTest {

    //For displaying results:
    @ParameterizedTest(name = "{index} gets {0} ride status")
    //For executing the test with these parameter each time
    @ValueSource(strings = {"rollercoaster", "logflume", "teacups"})
    //And applies to this test
    public void getsRideStatus(String ride) {
        RideStatusService rideStatusService = new RideStatusService();
        String rideStatus = rideStatusService.getRideStatus(ride);
        assertNotNull(rideStatus);
    }

    @Test
    public void unknownRideCausesFailure() {
        RideStatusService rideStatusService = new RideStatusService();
        //An exception must be raised if the parameter is not good!
        assertThrows(IllegalArgumentException.class, () -> {
            rideStatusService.getRideStatus("dodgems");
        });
    }
}

Saving project to git:
git init                 Initialises
git add .     If you don't execute it, Git does not save anything!!!
git status
git commit -m "Create Java Project for all of you."


13. Application plugin (run java program)


plugins {
    id 'application'
}

Included Tasks:
1. run: executes the java application. ./gradlew run .

Defining the main class:
It has been defined previously, in the "jar" task how to set the "main" class, but in the application plugin, it is defined ALSO in the "application" section

aplication {
    mainClass 'org.ximo.MyMainClass'
}

tasks.name ('jar') {
    manifest {
        attributes('Main-Class': 'org.ximo.MyMainClass')
    }
}

Running the application with parameters creating a new task:
Execute the "run" task and don't forget to pass parameters! Use --args   
In Eclipse you should run the application in a terminal using  ./gradlew run --args myParameter .
I have not been able to add arguments to gradle run task in the gradle task window in Eclipse!!!. 

Tom Gregory suggests adding arguments by updating the "javaExec" task in the build.gradle file, redefining the jar of the application and the runtime jars and also the main class!!!

tasks.register('runJar', JavaExec) {// Many redundances !!!!!
    group 'My Own Tasks'    // show tasks grouped when using "gradle tasks"
    description 'execute with arguments'
    // Uses the ouput from jar task (only the outputs
classpath tasks.named('jar').map { it.outputs } // collects main jar
    classpath configurations.runtimeClasspath // collects runtime jar dependencies
    // Add arguments to the run
    args 'myParameter'
    mainClass = 'org.ximo.MyMainClass' // reenter the main class !!!
}

Debugging the application using 2 ways:
1. executing  ./gradlew runJar --debug-jvm   and open IDE on port 5005 (this can be tricky as the java compiler of gradlew and project must be the same. Maybe intelliJ can be better for this.



2. Debug as an application in Eclipse (but it can be tricky)


The process of debugging. Using Jupiter, JUnit, and Testng
1. Jupiter

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2'
}

tasks.withType(Test).configureEach {
    useJUnitPlatform()
}
2. JUnit

dependencies {
    testImplementation 'junit:junit:4.3.12'
}

tasks.withType(Test).configureEach { // Empty
}
3. TestNG

dependencies {
    testImplementation 'org.testng.testng:7.7.0'
}

tasks.withType(Test).configureEach { 
    useTestNG()
}


Including and excluding only some classes for testing

tasks.withType(Test).configureEach {
    useJUnitPlatform()
    include '**/*MyTest*'
    exclude '**/*OtherTest*'
}

Executing a single test 
 ./gradlew test --test MyTest                                executes a class for testing
 ./gradlew test --test MyTest.someMethod           executes a method of a class for testing
 ./gradlew test --test MyTest.*someMethod*       executes matching method of a class for testing
 ./gradlew test --test org.tests.MyTest.someMethod  fully qualified name

Executing a test from zero (cleanTest option)
Only a test is executed if there have been changes, or the clean task has been previously used. If you want to execute it without making changes 
 ./gradlew cleanTest test 

Eclipse IDE executing a test method in the editor window
Right click on the method name of the source editor window and select "Run as -> JUnit Test"

13.1 JVM Test Suite plugin (Gradle>=7.3)

Applied automatically with java plugin
Each test suite has source directory and task
"test" is the default test suite for unit tests.

Define the required tests in the "testing" section, and can supply specific dependencies

testing {
    // Define the required integration tests
    suites {
        integrationTest(JvmTestSuite) {
            //Define internal dependencies
            dependencies {
                // Depends on the production code
                implementation project
            }
        }
    }
}

And is available in the tasks (verification-integrationTest)

Create a source folder for testing "integrationTest":
1. Create the folder src/integrationTest/java
2. Add the package com.gradlehero.themepark
3. Add a "source folder" and select the created folder "src/integrationTest/java"


Task check
In the the Gradle task window, in the verification folder, there is the check task that can be set to execute the integration test, used before committing code or continuous integration pipeline.

tasks.named('check') {
    dependsOn testing.suites.integrationTest
}


13.2 Java Version

Take care to have the same Java version for
1. Eclipse workspace settings
2. ./gradlew
3. build.gradle

In Eclipse, right click on the project and select "properties->Gradle" and select the convenient Java version


In gradlew, execute ./gradlew --version  and we get

to change the java version, in the terminal type

 export JAVA_HOME=<your new java path>

In the build.gradle we can set the java version as java-toolchain

java {
    toolchain {
        // Forces gradlew to use version 17 independenly of the JAVA_PATH!!!!
        // If not found this java version, gradlew DONWLOADS it
        languageVersion = JavaLanguageVersion.of(17)
    } 
}


Select another java version for executing the application:
Although we have the previous java-toolchain, we can additionally select another java version for executing

java {
    toolchain {
        // Forces gradlew to use version 17 independenly of the JAVA_PATH!!!!
        // If not found this java version, gradlew DONWLOADS it
        languageVersion = JavaLanguageVersion.of(17)
    } 
}

// Forces gradle to execute the application with Java 18 although we have
// compiled the application with version 17!!!!!
tasks.withType(JavaExec).configureEach {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(18)
    }
}



Select different java versions for different tasks:

// Compile the application with version 17!!!!!
tasks.withType(JavaCompile).configureEach {
    javaCompiler = javaToolchains.compilerFor {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

// Forces this test task to be executed with java 18
tasks('myTest', Test) {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(18)
    }
}

14. maven-publish plugin 


plugins {
    id 'application'
    id 'maven-publish'
}
Identifying artifacts to publish and the destination:
Specify: group, version, publications and destination repository (if repository is remote)!

// You must supply a group and a version for publishing
group 'com.gradlehero'
version '0.1.0-SNAPSHOT'

// Publishing part
publishing {
	// Select from
	publications{
		
		maven(MavenPublication) {
		    // select components to publish
			from components.java //java file created by the java task
		}
	}
	//destination
	repositories {
		maven {
			url 'https://<your remote repository>'
			credentials {
				username "myUser"
				// password set by an environment variable
				password System.env.CODEARTIFACT_AUTH_TOKEN
			}
		}
	}
}


Publishing : execute ./gradlew publish

14.1 Local maven repository

There is a local repository, so try to execute in the terminal
ls -l ~/.m2/repository

Publish to maven local using  ./gradlew publishToMavenLocal
and verify that the project has been added
ls -l ~/.m2/repository/com/gradlehero


15. Spring Boot applications

15.1 With java plugin

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:2.5.3'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}
//=====================================
//   ThemeParkApplication
//=====================================
package com.gradlehero.themepark;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ThemeParkApplication {
    public static void main(String[] args) {
        SpringApplication.run(ThemeParkApplication.class);
    }
}

//=====================================
//   ThemeParkRide
//=====================================

package com.gradlehero.themepark;

public record ThemeParkRide(String name, String description) {
}

//=====================================
//   ThemeParkRideController
//=====================================

package com.gradlehero.themepark;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.Iterator;

@RestController
public class ThemeParkRideController {
    @GetMapping(path = "/rides")
    public Iterator<ThemeParkRide> getRides() {
        return Arrays.asList(
                new ThemeParkRide("Rollercoaster", "Train ride that speeds you along."),
                new ThemeParkRide("Log flume", "Boat ride with plenty of splashes."),
                new ThemeParkRide("Teacups", "Spinning ride in a giant tea-cup.")
        ).iterator();
    }
}

Run the application by right-clicking the ThemeParkApplication class and "run -run as java application"

If we change the java plugin with the application plugin we need to add this code to the build.gradle

application {
    mainClass = 'com.gradlehero.themepark.ThemeParkApplication'
}




15.2 With springframework plugin

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.5.3'
    // takes the version from springframework plugin
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'

}

repositories {
    mavenCentral()
}

dependencies {
    // takes the version from springframework plugin
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

Run the application with   ./gradlew bootRun

16. java-library plugin


plugins {
    id 'java-library'
}
Concepts:
- direct dependency (used explicitly by the application)
- transitive dependency (used by a dependency that is used by the application)

Dependency resolution:
- Only one version per group and name in dependencies.
- Gradle chooses the highest version of the dependency.


api vs implementation:
When creating a library, if the interface of any class or method in the created library contains any reference to a used library, then when declaring the dependency use api.
If our created library does not use any reference to the internally used library, then use implentation.
But this differentiation can be tricky in certain circumstances.

16.1 Special scenarios for dependency conflicts.

1. Simple exclude (transitive) of an unneeded dependency: We are sure that a transitive dependency of a direct dependency is not used. We can exclude it.

dependencies {
    implementation 'com.google.guava:guava:31.0.1-jre' {
        // unneeded dependency
        exclude group: 'com.google.code.findbugs', module; 'jsr305'
    }
2. Version upgrade (transitive): A transitive dependency (of a direct dependency) is buggy and a new version is available. 

dependencies {
    implementation 'com.google.guava:guava:31.0.1-jre'
    // change to a newer version not buggy. Does not work for version DOWNGRADE!!!
    constrains {
        implementation ('org.checkerframework:checker-qual:3.13.0') {
            because ('previous versions have a security vulnerability')
        }
    }
3. Version conflict (of a transitive dependency with direct dependency): A transitive dependency (of a direct dependency) conflicts with a direct dependency that has a different group and name. 
There are 2 solutions for this:
a. Using configuration-level exclude.
b. Module replacement.

dependencies {
    // uses "starter.logging"
    implementation 'org.springframework.boot:spring-boot-starter-web:2.6.2'
    // Conflicts with "starter.logging"
    implementation 'org.springframework.boot:spring-boot-starter-log4j2:2.5.2:2.6.2'
    
    // Option a. Exclude starter-logging. Never will be used!!!!
    configuration.implementation {
        exclude group: 'org.springframework.boot', module:'spring-boot-starter-logging'
    } 

    // Option b. Module replacement
    modules {
        module( 'org.springframework.boot:spring-boot-starter-logging')
            replacedBy ''org.springframework.boot:spring-boot-starter-log4j2'. 'Use Log42 instead of logback'
        }
    }

16.2 Dependencies task

Run   ./gradlew dependencies  to show all transitive dependencies tree of all direct dependencies
The omitted dependencies are marked as (*).
The not resolved dependencies are marked as (n).

Run   ./gradlew dependencies --configuration compileClasspath  to show all transitive dependencies tree of all direct dependencies during the compilation process

Run   ./gradlew dependencies --dependency org.slf4j:slf4j-api --configuration compileClasspath  to show all versions of a dependency used transitively or directly during the compilation process, without informing the library that uses it

When a dependency cannot be resolved, it is marked as FAILED.



17. Logging instead of println

17.1 logger:

There are 6 log levels: (1) Error, (2) Quiet (--quiet), (3) warn (--warn), (4) lifecicle (default), (5) info (--info), (6) debug (--debug)

first, create a task and use for instance 
logger.info 'Some data {}', System.currentTimerMillis()

where you can replace ".info" with ".debug" or any other level of log

tasks.register('logTest') {
    doLast {
        logger.info 'Some data {}', System.currenttimeMillis()
    }    
 
}
and now run it indicating the log level of "info" or a down level (e.g. debug) in order to see the messages.

./gradlew logTest --debug

17.2 Detecting errors:

1. Clear (syntax) error detected by task build:
If you write "implementatino" instead of implementation, gradle complains "Could not find method implementatino()", but does not say at what line it is, you need to search it whit an editor.

2. Error in the code of a custom tag being executed:
You can add --stacktrace option to show more info ./gradlew mytask --stacktrace and displays the line number of the error 

3. Debug out compiler options:
You can see whow your code is being compiled
./gradlew compileJava --debug | grep "Compiler arguments"

3. Log out dependency resolutions:
You can force gradle to refresh the dependencies from repository and show the latest version
./gradlew compileJava --refresh-dependencies --info

18. Password protection in build.gradle

18.1 defining and passing parameters to execute a task:

Place parameters mavenUserName and mavenPassword parameters instead of 'myUser' and 'myPassword' 

publishing {
	publications{
		maven(MavenPublication) {
			from components.java //java file created by the java task
		}
	}
	repositories {
		maven {
			url 'https://<your remote repository>'
			credentials {
                                // username and password ara parameters      
				username mavenUserName  //'myUser'
				password mavenPassword  //'myPassword'
			}
		}
	}
}

call the task as follows
./gradlew publish -PmavenUserName=myUser -PmavenPassword=myPassword


19. ProjectProperties

19.1 Passing properties for executing a task

1. On  the command line using -P 
./gradlew <task-name> -PmyPropName=myProvValue

2. As java system property using -D
./gradlew <task-name> -Dorg.gradle.project.myPropName=myProvValue

3. As environment variables
ORG_GRADLE_PROJECT_myPropName=myProvValue ./gradlew <task-name> 

4. In the file gradle.properties
myPropName=myProvValue

19.1 Accesing properties in the definition of tasks (build.maven)

1. Directly as a variable
println myPropName

2. Using property method
println project.property('myPropName')

3. Using findProperty method
println project.findProperty('myPropName')

4. Using Elvis operator to return a default value
println project.findProperty('myPropName') ?: 'default value'

5. Check if exist a property with hasProperty method
if (hasProperty('myPropName') {...