Maven en tu flujo de trabajo de CI/CD: guía para mejorar tus procesos de desarrollo
Maven en tu flujo de trabajo de CI/CD: guía para mejorar tus procesos de desarrollo En el mundo actual del desarrollo de software, la integración…
Cuando trabajamos en Maven es muy habitual necesitar agregar y configurar complementos (plugins) al mismo para poder usarlos para realizar tareas de compilación necesarias. Maven usa una arquitectura de plugins, y a excepción de las funcionalidades prioritarias, todas las tareas en Maven se realizan mediante plugins. Es sobre lo último que centraremos en Maven clean plugin.
Cuando se construye un proyecto, es importante asegurarse de que no se está afectado anteriormente por artefactos de una construcción anterior. Por lo general, las herramientas de compilación guardan artefactos en una carpeta bien definida llamada target, dependiendo del directorio de trabajo del proyecto. Antes de una nueva compilación, esta carpeta generalmente se elimina.
Cuando se invoca a la fase clean, Maven borra automáticamente el resultado de la compilación anterior (en Maven se conoce a la fase de limpieza como clean). Para ello se utiliza el plugin Maven Clean. El plugin maven-clean-plugin se encarga de esta tarea: limpia el directorio de trabajo que ya veremos que se llama target.
Como clean es un ciclo de vida aparte del ciclo de vida estándar (predeterminado/default), clean debe llamarse explícitamente antes del ciclo de vida predeterminado (default) si necesitas asegurarte de que se elimine el directorio de índices.
Antes de profundizar en el plugin, es importante entender qué genera Maven dentro de la carpeta target. Cuando ejecutamos una compilación, Maven deposita en este directorio todos los artefactos resultantes del proceso:
.class compilados a partir del código fuente ubicado en src/main/java..class compilados de los tests ubicados en src/test/java..jar, .war o .ear resultante de la fase package, con el nombre definido por artifactId y version.Con el tiempo, estos artefactos pueden quedar desactualizados si no se limpia el directorio antes de una nueva compilación. Por ejemplo, si renombramos una clase y no ejecutamos clean, el archivo .class antiguo seguirá existiendo junto al nuevo, lo que puede provocar errores difíciles de diagnosticar. De ahí la importancia del plugin maven-clean-plugin.
Vamos en la práctica a ver cómo trabajar con el plugin maven-clean-plugin. Para ello vamos a crear un proyecto nuevo con el siguiente comando:
mvn archetype:generate -DgroupId=com.josemtech.maven \
-DartifactId=mi-proyecto \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.5 \
-DinteractiveMode=false
Para poder ejecutar el código anterior necesitamos tener instalado Maven en nuestra computadora y además tener bien configuradas las variables de entorno para que se reconozca el comando mvn.
Si tenemos todos los requisitos y ejecutamos el comando anterior, deberíamos ver una salida similar a la siguiente:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> archetype:3.2.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO] <<< archetype:3.2.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO] --- archetype:3.2.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] Using following parameters for creating project from Archetype: maven-archetype-quickstart:1.4
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.josemtech.maven
[INFO] Parameter: artifactId, Value: mi-proyecto
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.josemtech.maven
[INFO] Parameter: packageInPathFormat, Value: com/josemtech/maven
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.josemtech.maven
[INFO] Parameter: groupId, Value: com.josemtech.maven
[INFO] Parameter: artifactId, Value: mi-proyecto
[INFO] Project created from Archetype in dir: /Users/jose/Desktop/test-maven/mi-proyecto
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.892 s
[INFO] Finished at: 2023-05-18T23:07:00-05:00
[INFO] ------------------------------------------------------------------------
Si nos desplazamos dentro de la carpeta del proyecto observaremos cómo no existe la carpeta target. Para generar los artefactos de este proyecto y que aparezca la carpeta target vamos a ejecutar mvn package.
Ahora se ha generado el directorio target como se observa a continuación:
mi-proyecto/
├── pom.xml
├── src/
└── target/
Ahora ya podemos ejecutar el comando mvn clean y veamos cómo este elimina la carpeta target del proyecto. Deberíamos ver en el terminal el siguiente resultado:
[INFO] --- clean:3.1.0:clean (default-clean) @ mi-proyecto ---
[INFO] Deleting /Users/jose/Desktop/test-maven/mi-proyecto/target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.779 s
[INFO] Finished at: 2023-05-18T23:18:12-05:00
[INFO] ------------------------------------------------------------------------
Para este punto ya no deberíamos tener la carpeta target en la carpeta de nuestro proyecto:
mi-proyecto/
├── pom.xml
└── src/
Podría darse el caso de que deseemos que cada vez que construyamos nuestro proyecto nos aseguremos que se borre la carpeta target antes de construir el proyecto. Veamos a continuación cómo podemos ejecutar automáticamente el plugin clean durante la compilación del proyecto.
Para poder ejecutar el plugin clean automáticamente durante la compilación del proyecto necesitamos definir el plugin con algunos parámetros en nuestro archivo pom. Veamos la forma en que podemos agregarlo:
<build>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>auto-clean</id>
<phase>initialize</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Ahora ejecutaremos de nuevo el comando mvn package. Lo que hemos hecho es solicitarle a Maven que ejecute el goal clean durante la fase de inicialización del proyecto. Además podemos identificar esta ejecución con el id auto-clean como se muestra a continuación:
[INFO] ------------------< com.josemtech.maven:mi-proyecto >-------------------
[INFO] Building mi-proyecto 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.1.0:clean (auto-clean) @ mi-proyecto ---
[INFO]
[INFO] --- resources:3.0.2:resources (default-resources) @ mi-proyecto ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/jose/Desktop/test-maven/mi-proyecto/src/main/resources
[INFO]
[INFO] --- compiler:3.8.0:compile (default-compile) @ mi-proyecto ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/jose/Desktop/test-maven/mi-proyecto/target/classes
[INFO]
[INFO] --- resources:3.0.2:testResources (default-testResources) @ mi-proyecto ---
Sería de mucha utilidad que pudiéramos eliminar carpetas adicionales, es decir, otras carpetas que no sean la carpeta target durante la ejecución del plugin clean, veamos cómo podemos lograr esto.
Para poder eliminar carpetas además de la carpeta target debemos incluir las siguientes líneas en el pom:
<build>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>auto-clean</id>
<phase>initialize</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
<configuration>
<filesets>
<fileset>
<directory>borrar</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
Creamos una carpeta llamada borrar dentro de nuestro proyecto y lo que deseamos es que cuando se ejecute el plugin clean se borre también esta carpeta.
mi-proyecto/
├── borrar/
├── pom.xml
├── src/
└── target/
Ejecutamos nuevamente el comando mvn package y deberíamos ver un resultado como el siguiente en nuestro terminal:
[INFO] ------------------< com.josemtech.maven:mi-proyecto >-------------------
[INFO] Building mi-proyecto 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.1.0:clean (auto-clean) @ mi-proyecto ---
[INFO] Deleting /Users/jose/Desktop/test-maven/mi-proyecto/target
[INFO] Deleting /Users/jose/Desktop/test-maven/mi-proyecto/borrar (includes = [], excludes = [])
[INFO]
[INFO] --- resources:3.0.2:resources (default-resources) @ mi-proyecto ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/jose/Desktop/test-maven/mi-proyecto/src/main/resources
Observemos cómo se han borrado las carpetas target y borrar cuando hemos realizado un mvn package, debemos recordar que esto sucede porque estamos ejecutando el plugin en la fase de inicialización.
El elemento <filesets> ofrece más control del que vimos en el ejemplo anterior. Además de especificar un directorio completo, podemos utilizar <includes> y <excludes> para filtrar exactamente qué archivos o patrones eliminar. Esto resulta útil cuando no queremos borrar todo el contenido de una carpeta, sino solo ciertos tipos de archivo.
Por ejemplo, si queremos eliminar únicamente los archivos .log dentro de una carpeta llamada logs, pero conservar el resto de archivos, podemos configurar el plugin de la siguiente forma:
<configuration>
<filesets>
<fileset>
<directory>logs</directory>
<includes>
<include>**/*.log</include>
</includes>
</fileset>
</filesets>
</configuration>
También podemos combinar <includes> con <excludes> para crear reglas más precisas. Por ejemplo, eliminar todos los archivos temporales excepto un archivo de configuración específico:
<configuration>
<filesets>
<fileset>
<directory>temp</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>config.properties</exclude>
</excludes>
</fileset>
</filesets>
</configuration>
Los patrones utilizan la sintaxis estándar de Ant: * coincide con cualquier secuencia de caracteres dentro de un nivel de directorio, y ** coincide con cualquier secuencia de directorios.
Una duda muy común entre desarrolladores que están comenzando con Maven es la diferencia entre ejecutar mvn clean solo y ejecutar mvn clean package.
La clave está en entender que Maven tiene tres ciclos de vida independientes:
validate, compile, test, package, verify, install y deploy.Cuando ejecutamos mvn clean, únicamente invocamos el ciclo de vida clean, que ejecuta la fase pre-clean y luego clean (eliminando el directorio target). El proyecto no se vuelve a compilar.
Cuando ejecutamos mvn clean package, estamos invocando dos ciclos de vida en secuencia: primero el ciclo clean (que borra target) y luego el ciclo default hasta la fase package (que compila, ejecuta tests y empaqueta el artefacto). Esto garantiza una compilación completamente limpia.
La recomendación general es utilizar siempre mvn clean antes de cualquier fase del ciclo default cuando queremos asegurarnos de que no hay artefactos residuales. Los comandos más habituales son:
mvn clean compile — Limpia y compila el código fuente.mvn clean test — Limpia, compila y ejecuta los tests.mvn clean package — Limpia, compila, ejecuta tests y genera el artefacto empaquetado.mvn clean install — Limpia, compila, ejecuta tests, empaqueta e instala en el repositorio local.Omitir la ejecución de clean antes de compilar puede parecer inofensivo, pero en la práctica puede provocar problemas difíciles de diagnosticar:
clean, el archivo .class antiguo permanece en target/classes. Esto puede causar errores de compilación confusos o, peor aún, que el programa se ejecute usando la clase obsoleta sin que nos demos cuenta..class compilado sigue en target/test-classes, Maven podría ejecutarlo igualmente, dando resultados engañosos.application.properties o archivos XML) que fueron modificados o eliminados pueden seguir presentes en su versión anterior dentro de target, causando comportamientos inesperados en tiempo de ejecución..jar o .war generado podría incluir clases y recursos que ya no forman parte del proyecto, incrementando su tamaño innecesariamente y potencialmente introduciendo vulnerabilidades de seguridad.La regla práctica es sencilla: si algo no funciona como esperas después de un cambio, ejecuta mvn clean y vuelve a compilar. En entornos de integración continua (CI/CD), lo habitual es que el pipeline siempre comience con mvn clean para garantizar builds reproducibles.