<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nándor Holozsnyák</title>
    <description>The latest articles on DEV Community by Nándor Holozsnyák (@nandorholozsnyak).</description>
    <link>https://dev.to/nandorholozsnyak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F139833%2F7aeae520-bab5-4677-8eab-4daa682838a6.png</url>
      <title>DEV Community: Nándor Holozsnyák</title>
      <link>https://dev.to/nandorholozsnyak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nandorholozsnyak"/>
    <language>en</language>
    <item>
      <title>Testing with Maven - Organizing unit and integration tests</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Thu, 08 Jun 2023 20:36:00 +0000</pubDate>
      <link>https://dev.to/rodnan-sol/testing-with-maven-organizing-unit-and-integration-tests-35oh</link>
      <guid>https://dev.to/rodnan-sol/testing-with-maven-organizing-unit-and-integration-tests-35oh</guid>
      <description>&lt;h2&gt;
  
  
  Testing with Maven - Organizing unit and integration tests
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Testing with Maven - Organizing unit and integration tests

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;How does a Maven project look like?&lt;/li&gt;
&lt;li&gt;What does Maven Surefire plugin do?&lt;/li&gt;
&lt;li&gt;Why isn't Surefire plugin good for running integration tests?&lt;/li&gt;
&lt;li&gt;Maven Failsafe Plugin&lt;/li&gt;
&lt;li&gt;How does Maven Failsafe plugin work?&lt;/li&gt;
&lt;li&gt;
How to organize tests properly in your Maven project?

&lt;ul&gt;
&lt;li&gt;Mix unit and integration tests&lt;/li&gt;
&lt;li&gt;Advantages&lt;/li&gt;
&lt;li&gt;Disadvantages&lt;/li&gt;
&lt;li&gt;Separate unit and integration tests on package level&lt;/li&gt;
&lt;li&gt;Advantages&lt;/li&gt;
&lt;li&gt;Disadvantages&lt;/li&gt;
&lt;li&gt;Separate unit and integration tests on root folder level&lt;/li&gt;
&lt;li&gt;
How to set it up?

&lt;ul&gt;
&lt;li&gt;Maven Compiler Plugin Way&lt;/li&gt;
&lt;li&gt;build-helper-maven-plugin way&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Advantages&lt;/li&gt;

&lt;li&gt;Disadvantages&lt;/li&gt;

&lt;li&gt;Surprise option: New maven module&lt;/li&gt;

&lt;li&gt;Advantages&lt;/li&gt;

&lt;li&gt;Disadvantages&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;li&gt;Follow Rodnan Sol for more&lt;/li&gt;

&lt;li&gt;Follow the author too&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Are you making unit and integration tests in your project? I hope so! &lt;/p&gt;

&lt;p&gt;Tests are the fundamentals of a good working and quality software, nobody shouls be skipping them, even if their clients says it 🔥.&lt;/p&gt;

&lt;p&gt;If you are a Java developer you probably know the different build tools out there and their support for writing and running unit and integration tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ant&lt;/li&gt;
&lt;li&gt;Maven&lt;/li&gt;
&lt;li&gt;Gradle&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You name it&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post we will be focusing on &lt;strong&gt;Maven&lt;/strong&gt;, built and maintained by ASF (Apache Software Foundation). &lt;/p&gt;

&lt;p&gt;Maven is a beautiful project that helped millions of projects go live, it is an industry standard, but nothing is perfect even millions of projects are depending on it. In this post we are not going to find all the negativity behind the Maven build tool, only just to focus on the testing topics, and to give you advices how your projects structure could be setup to enforce a better test and production code quality.&lt;/p&gt;

&lt;p&gt;If you are familiar with a setup of a Maven project, and you know how the different plugins like the Surefire and Failsafe handle the compiled test classes, then you can skip to the following section, the very first few ones are just introducing how Maven is handling the tests. If you want to refresh your knowledge do read the first chapters too! :)&lt;/p&gt;

&lt;h3&gt;
  
  
  How does a Maven project look like?
&lt;/h3&gt;

&lt;p&gt;By default, if a new projects is just initialized, it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf8jfbbdtel1moh6q7g6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf8jfbbdtel1moh6q7g6.png" alt="Basic Project Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;UserService&lt;/code&gt; classes are just here for the example, there is no framework behind them, there is no real logic, only for the sake of demonstration.&lt;/p&gt;

&lt;p&gt;How can we execute the tests that are in our project? That is easy, just run the &lt;code&gt;mvn test&lt;/code&gt; command in your terminal.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;mvn &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Scanning &lt;span class="k"&gt;for &lt;/span&gt;projects...
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;------------------&lt;/span&gt;&amp;lt; org.rodnansol:simple-test-setup &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;-------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Building simple-test-setup 1.0-SNAPSHOT
&lt;span class="o"&gt;[&lt;/span&gt;INFO]   from pom.xml
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;--------------------------------&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; jar &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nt"&gt;---------------------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;---&lt;/span&gt; resources:3.3.0:resources &lt;span class="o"&gt;(&lt;/span&gt;default-resources&lt;span class="o"&gt;)&lt;/span&gt; @ simple-test-setup &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;WARNING] Using platform encoding &lt;span class="o"&gt;(&lt;/span&gt;UTF-8 actually&lt;span class="o"&gt;)&lt;/span&gt; to copy filtered resources, i.e. build is platform dependent!
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Copying 0 resource
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;---&lt;/span&gt; compiler:3.10.1:compile &lt;span class="o"&gt;(&lt;/span&gt;default-compile&lt;span class="o"&gt;)&lt;/span&gt; @ simple-test-setup &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Nothing to compile - all classes are up to &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;---&lt;/span&gt; resources:3.3.0:testResources &lt;span class="o"&gt;(&lt;/span&gt;default-testResources&lt;span class="o"&gt;)&lt;/span&gt; @ simple-test-setup &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;WARNING] Using platform encoding &lt;span class="o"&gt;(&lt;/span&gt;UTF-8 actually&lt;span class="o"&gt;)&lt;/span&gt; to copy filtered resources, i.e. build is platform dependent!
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;---&lt;/span&gt; compiler:3.10.1:testCompile &lt;span class="o"&gt;(&lt;/span&gt;default-testCompile&lt;span class="o"&gt;)&lt;/span&gt; @ simple-test-setup &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Nothing to compile - all classes are up to &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;---&lt;/span&gt; surefire:3.0.0-M8:test &lt;span class="o"&gt;(&lt;/span&gt;default-test&lt;span class="o"&gt;)&lt;/span&gt; @ simple-test-setup &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO]  T E S T S
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Running org.rodnansol.user.UserServiceTest
OpenJDK 64-Bit Server VM warning: Sharing is only supported &lt;span class="k"&gt;for &lt;/span&gt;boot loader classes because bootstrap classpath has been appended
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.645 s - &lt;span class="k"&gt;in &lt;/span&gt;org.rodnansol.user.UserServiceTest
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Results:
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
&lt;span class="o"&gt;[&lt;/span&gt;INFO]
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] BUILD SUCCESS
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Total &lt;span class="nb"&gt;time&lt;/span&gt;:  1.629 s
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Finished at: 2023-06-04T17:45:29+02:00
&lt;span class="o"&gt;[&lt;/span&gt;INFO] &lt;span class="nt"&gt;------------------------------------------------------------------------&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;What happened in this case?&lt;/p&gt;

&lt;p&gt;Maven has multiple &lt;strong&gt;phases&lt;/strong&gt; that can be executed, as Maven has the &lt;code&gt;pom.xml&lt;/code&gt; file, it also has a so called &lt;strong&gt;Effective POM&lt;/strong&gt; that contains all the phase configurations, but they are not presented in the &lt;code&gt;pom.xml&lt;/code&gt; file because that would be too verbose, and it would be difficult to read. These configurations are inherited from the so-called &lt;strong&gt;Super POM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Effective POM&lt;/strong&gt; contains all the plugins that will be run when a specific phase is executed, the phases are backed by plugins, like the &lt;strong&gt;maven-clean-plugin&lt;/strong&gt;, &lt;strong&gt;maven-compiler-plugin&lt;/strong&gt; and the &lt;strong&gt;maven-surefire-plugin&lt;/strong&gt; for example that is executing any tests.&lt;/p&gt;

&lt;p&gt;Do not here is that the &lt;strong&gt;maven-surefire-plugin&lt;/strong&gt; executes its &lt;code&gt;test&lt;/code&gt; goal in the &lt;code&gt;test&lt;/code&gt; phase. Even if they are having the same wording it is not the same. The other thing is also crucial, we do not see the &lt;code&gt;maven-failsafe-plugin&lt;/code&gt; here, which will be a key later.&lt;/p&gt;

&lt;p&gt;Okay, okay, I want to see it with my eyes, so how to create the effective-pom? It is easy: &lt;code&gt;mvn help:effective-pom&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As it is being more than 250 lines, let me just copy a small section of it.&lt;/p&gt;

&lt;p&gt;We can see the configuration of the &lt;strong&gt;maven-clean-plugin&lt;/strong&gt; and the &lt;strong&gt;maven-surefire-plugin&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4p7x4qcoh468158saha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4p7x4qcoh468158saha.png" alt="Effective Pom"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  What does Maven Surefire plugin do?
&lt;/h3&gt;

&lt;p&gt;TL;DR: It executes tests.&lt;/p&gt;

&lt;p&gt;Seriously, it is executing tests, but how does it recognize tests?&lt;/p&gt;

&lt;p&gt;The plugin has configuration values, and one of them first defines the folder of the compiled test classes and the other configuration options sets the included files, it is done mostly by patterns.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test class directory: &lt;a href="https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#testclassesdirectory" rel="noopener noreferrer"&gt;https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#testclassesdirectory&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;By default: &lt;code&gt;target/test-classes&lt;/code&gt; - and it is compiled by the &lt;strong&gt;maven-compiler-plugin&lt;/strong&gt;'s &lt;strong&gt;testCompile&lt;/strong&gt; goal at the &lt;strong&gt;test-compile&lt;/strong&gt; phase. A lots of things are going there for sure. 🔥&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Included classes: &lt;a href="https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#includes" rel="noopener noreferrer"&gt;https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#includes&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;By default any Java file that ends with: Test*.java *Test.java, *Tests.java, *TestCase.java.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now may come the &lt;strong&gt;AHA!&lt;/strong&gt; moment, "so this is why my shiny JUnit test was not executed, because I named it UserServiceT or UserServiceTE".&lt;/p&gt;

&lt;p&gt;Yes! But you can configure it to include new patterns or specific files can be put into the include section.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why isn't Surefire plugin good for running integration tests?
&lt;/h3&gt;

&lt;p&gt;If we check the default lifecycle, we will see a list of the available phases: &lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#default-lifecycle" rel="noopener noreferrer"&gt;
      maven.apache.org
    &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6lu0p0pq53ixcnk17d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6lu0p0pq53ixcnk17d0.png" alt="Maven Default lifecycle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's check the &lt;code&gt;test&lt;/code&gt; phase again. Before this phase we can only see the &lt;code&gt;test-compile&lt;/code&gt; which is necessary to compile the test classes, we definitely see some other phases, but they are not that "interesting" for us now.&lt;/p&gt;

&lt;p&gt;After that we see no "test" related phases, but the &lt;code&gt;integration-test&lt;/code&gt; phase, which is not associated with the Surefire plugin by default.&lt;/p&gt;

&lt;p&gt;What if we want to run integration tests, that would require different environmental setups, like running a &lt;strong&gt;docker-compose.yml&lt;/strong&gt; file before running any integration test, and stopping the started dependencies when the integration tests finished?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maven Surefire is not created for running integration tests, this is why it is not picking up test classes having the IT,ITCase postfix in their names, for that Maven Failsafe plugin should be used!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;📝 One small note here: You definitely can configure the Surefire plugin to handle the test classes ending with IT,ITCase, but it is just not meant to be used with them. On the other hand, integration tests also can be named as simple Test,Tests,TestCase, but they are different, please do not mix your unit and integration tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maven Failsafe Plugin
&lt;/h3&gt;

&lt;p&gt;Let me quote the following from the Failsafe plugin's documentation:&lt;/p&gt;

&lt;p&gt;"The Failsafe Plugin is designed to run integration tests while the Surefire Plugin is designed to run unit tests. The name (failsafe) was chosen both because it is a synonym of surefire and because it implies that when it fails, it does so in a safe way.&lt;/p&gt;

&lt;p&gt;The Maven lifecycle has four phases for running integration tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pre-integration-test&lt;/strong&gt; for setting up the integration test environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;integration-test&lt;/strong&gt; for running the integration tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;post-integration-test&lt;/strong&gt; for tearing down the integration test environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;verify&lt;/strong&gt; for checking the results of the integration tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use the Surefire Plugin for running tests, then when you have a test failure, the build will stop at the integration-test phase and your integration test environment will not have been torn down correctly.&lt;/p&gt;

&lt;p&gt;The Failsafe Plugin is used during the integration-test and verify phases of the build lifecycle to execute the integration tests of an application. The Failsafe Plugin will not fail the build during the integration-test phase, thus enabling the post-integration-test phase to execute." - Source: &lt;a href="https://maven.apache.org/surefire/maven-failsafe-plugin/" rel="noopener noreferrer"&gt;https://maven.apache.org/surefire/maven-failsafe-plugin/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can not be described better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For unit test: Use &lt;strong&gt;Surefire&lt;/strong&gt; !&lt;/li&gt;
&lt;li&gt;For integration tests: Use &lt;strong&gt;Failsafe&lt;/strong&gt; ! &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How does Maven Failsafe plugin work?
&lt;/h3&gt;

&lt;p&gt;It is "almost" doing the same as the &lt;strong&gt;Surefire&lt;/strong&gt; plugin, it executes test from a specific directory and with a specific filename pattern.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test classes directory: &lt;a href="https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#testclassesdirectory" rel="noopener noreferrer"&gt;https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#testclassesdirectory&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;Same as for the &lt;strong&gt;Surefire&lt;/strong&gt;, so the &lt;code&gt;target/test-classes&lt;/code&gt;, the compiled source files are also being compiled by the &lt;strong&gt;Maven Compiler Plugin&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Included classes: &lt;a href="https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#includes" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#includes" rel="noopener noreferrer"&gt;https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#includes&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;This is where the difference can be seen easily, while the &lt;strong&gt;Surefire&lt;/strong&gt; plugin included java classes ending with Test,Tests,TestCase postfixes, in this case the following patterns are the default: IT*.java, *IT.java, *ITCase.java. - So for example &lt;code&gt;UserServiceIT.java&lt;/code&gt; or &lt;code&gt;CreateUserITCase.java&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The main point here is that by default unit and integration tests must be put into the "same" folder structure, so to the &lt;code&gt;src/tests&lt;/code&gt; folder. There are no &lt;code&gt;src/it&lt;/code&gt; or &lt;code&gt;src/it-test&lt;/code&gt; options by default, but it can changed easily, wait till the end.&lt;/p&gt;




&lt;h3&gt;
  
  
  How to organize tests properly in your Maven project?
&lt;/h3&gt;

&lt;p&gt;We have a few options, I will show you 3 that I'm aware of, and I will try to define the advantages and the disadvantages of the different setups.&lt;/p&gt;

&lt;p&gt;I want to make a note here, our goal is to &lt;strong&gt;not break the package setups&lt;/strong&gt;, so every time we write a unit or integration tests for a class, we would like to mirror the setup of the production and test code packages like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqginvuk0qcjrmmhvc0p3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqginvuk0qcjrmmhvc0p3.png" alt="Production and test code setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if we have a package like this: &lt;code&gt;org.rodnansol.user&lt;/code&gt; and we have a class in it named &lt;code&gt;UserService&lt;/code&gt; our &lt;code&gt;UserServiceTest&lt;/code&gt; or &lt;code&gt;UserServiceIT&lt;/code&gt; should end up in the &lt;code&gt;org.rodnansol.user&lt;/code&gt; package too.&lt;/p&gt;

&lt;p&gt;There are cases when our integrations tests (because we name them IT) are covering a different level in the level test pyramid, like full end 2 end tests, in those cases it is another topic how the classes are structured, but if you think about it, in those cases, you might not know about the code you are testing, in those cases it can be a blackbox, and it is up to you how you set it up.&lt;/p&gt;

&lt;p&gt;Quick spoiler: In the second option we will break it, but you will see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not forget to set up the Maven Failsafe plugin to run the tests!&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-failsafe-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.0-M8&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default-integration-test&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Mix unit and integration tests
&lt;/h4&gt;

&lt;p&gt;As its name say, mix the unit and integration tests, just by placing them next to each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcr49fcbud051nn8wl6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcr49fcbud051nn8wl6z.png" alt="Mixing unit and integration tests"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Tests can be found easily next to each other.&lt;/li&gt;
&lt;li&gt;Encapsulation should not be broken, so if the &lt;code&gt;UseService&lt;/code&gt; will only be used in the &lt;code&gt;user&lt;/code&gt; package, it can be &lt;strong&gt;package private&lt;/strong&gt;. Just because we are writing a test for a class, it does not mean that the encapsulation should be broken, it is also true for methods, fields, constants in classes. Always work for the narrowest scope, do not extend it just because a test needs something.

&lt;ul&gt;
&lt;li&gt;Yes, if you did not know, visibility in the production and test code are not different, so tests can access package private or protected members of the production code! Use it wisely!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The unit and integration tests are mixed, it can pollute the code and if the integration test is having different utility classes they may end up next to these classes, and with time and without proper maintenance the setup can be a mess.&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  Separate unit and integration tests on package level
&lt;/h4&gt;

&lt;p&gt;I have seen it on many projects where if we had a package structure like this: &lt;code&gt;org.rodnansol.user&lt;/code&gt; developers introduced a middle package that meant to be used by the integration tests for example: &lt;code&gt;org.rodnansol.it.user&lt;/code&gt; or &lt;code&gt;org.rodnansol.integrationtest.user&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2mrd0s3izcwpfguk9j1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2mrd0s3izcwpfguk9j1.png" alt="Different package for ITs"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Unit and integration tests are separated.&lt;/li&gt;
&lt;li&gt;Integration test related utilities can be put next to the integration test and they can be marked as package private, to make sure other tests will not use it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Encapsulation is broken! Not just a little, but way too much!

&lt;ul&gt;
&lt;li&gt;Our UserService class from now can not be package-private.&lt;/li&gt;
&lt;li&gt;None of the UserService methods can be package-private that will be used in the integration-test.&lt;/li&gt;
&lt;li&gt;If we want to play around with some dependency mocking if they reside in the same package as package-private classes, now they must be public to be accessible for the test.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if it seems tempting to use this kind of setup, please think about the consequences in the production code. Visibility modifiers are created to be used wisely and properly. Just because a test needs to test a method, it can not be a reason to change its visibility modifier! I would call it as a code smell!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please think twice before this option is going to be used.&lt;/strong&gt;&lt;/p&gt;


&lt;h4&gt;
  
  
  Separate unit and integration tests on root folder level
&lt;/h4&gt;

&lt;p&gt;From my point of view probably the "cleanest" solution! Separating the unit and integration tests on "root" folder level, like on the picture below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2riffsk63yq1nzqhzaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2riffsk63yq1nzqhzaj.png" alt="Separate unit and ITs on folder level"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main problem with this setup is that it does not work without some configuration. The &lt;strong&gt;Maven Compiler Plugin&lt;/strong&gt; compiles the Java classes in the following directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/main/java&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/test/java&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if we want to introduce a third folder we should be making changes even to the &lt;strong&gt;Maven Compiler Plugin,&lt;/strong&gt; or we have to trick the system, and we have to make sure even if the &lt;strong&gt;Maven Compiler Plugin&lt;/strong&gt;'s default configuration is not overwritten our Java classes and resource files are compiled and can be used by the &lt;strong&gt;Maven Failsafe Plugin&lt;/strong&gt;.&lt;/p&gt;
&lt;h5&gt;
  
  
  How to set it up?
&lt;/h5&gt;

&lt;p&gt;First of all create the following folder stucture for your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;project-root-level&amp;gt;&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;src&lt;/li&gt;
&lt;li&gt;it

&lt;ul&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;main

&lt;ul&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;test

&lt;ul&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;
  
  
  Maven Compiler Plugin Way
&lt;/h6&gt;

&lt;p&gt;Overwrite the default configuration of the &lt;strong&gt;maven-compiler-plugin&lt;/strong&gt; and for the &lt;code&gt;test-compile&lt;/code&gt; phase overwrite the &lt;code&gt;compileSourceRoots&lt;/code&gt; configuration values and add the following entries to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;compileSourceRoot&amp;gt;src/test/java&amp;lt;/compileSourceRoot&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;compileSourceRoot&amp;gt;src/it/java&amp;lt;/compileSourceRoot&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make sure any test related resources, that is not a Java file, is copied to the &lt;code&gt;target/test-classes&lt;/code&gt; folder, we must configure the &lt;code&gt;&amp;lt;testResources&amp;gt;&lt;/code&gt; tag too, to include the resources from the &lt;code&gt;src/it/resources&lt;/code&gt; folder too. It is done by the &lt;strong&gt;Maven Resources Plugin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Check out the full plugin configuration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;testResources&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;testResource&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;src/it/resources&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/testResource&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;testResource&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;src/test/resources&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/testResource&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/testResources&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-compiler-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default-testCompile&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;test-compile&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;testCompile&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;compileSourceRoots&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;compileSourceRoot&amp;gt;&lt;/span&gt;src/test/java&lt;span class="nt"&gt;&amp;lt;/compileSourceRoot&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;compileSourceRoot&amp;gt;&lt;/span&gt;src/it/java&lt;span class="nt"&gt;&amp;lt;/compileSourceRoot&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/compileSourceRoots&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-failsafe-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.0-M8&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default-integration-test&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h6&gt;
  
  
  build-helper-maven-plugin way
&lt;/h6&gt;

&lt;p&gt;In this case the &lt;strong&gt;maven-compiler-plugin&lt;/strong&gt; should not be overwritten, but a new plugin must be introduced to the whole project, it is called &lt;strong&gt;build-helper-maven-plugin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The folder setup is the same as before, but in this case with the plugin we hook two goals to the following phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate-test-sources - "generate any test source code for inclusion in compilation." - With this phase binding our classes from the &lt;code&gt;src/it/java&lt;/code&gt; will be compiled during the test-compile phase.&lt;/li&gt;
&lt;li&gt;generate-test-resources - "create resources for testing." - With this phase binding all the integration test related resources files are going to be copied to the &lt;code&gt;target/test-classes&lt;/code&gt; folder.

&lt;ul&gt;
&lt;li&gt;With the &lt;code&gt;&amp;lt;filtering&amp;gt;&lt;/code&gt; option if any of the resource files are containing variable substitutions (${}) maven will filter those values and replace them with values from the &lt;code&gt;pom.xml&lt;/code&gt; - this is optional.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the full plugin configuration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.mojo&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;build-helper-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.4.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;add-integration-test-sources&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;generate-test-sources&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;add-test-source&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;sources&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;source&amp;gt;&lt;/span&gt;src/it/java&lt;span class="nt"&gt;&amp;lt;/source&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/sources&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;add-integration-test-resources&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;generate-test-resources&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;add-test-resource&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;resources&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;resource&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;filtering&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/filtering&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;src/it/resources&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/resource&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-failsafe-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.0-M8&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default-integration-test&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After any of the plugin setups the following command should copy and compile any test sources and resources into the &lt;code&gt;target/test-classes&lt;/code&gt; folder: &lt;code&gt;mvn clean verify&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;verify&lt;/code&gt; phase will trigger the &lt;strong&gt;Maven Failsafe Plugin&lt;/strong&gt;'s &lt;code&gt;integration-test&lt;/code&gt; goal within the &lt;code&gt;integration-test&lt;/code&gt; phase.&lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;Small note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure the files under the &lt;code&gt;src/test&lt;/code&gt; and &lt;code&gt;src/it&lt;/code&gt; are having different names because they will overwrite each other, and it will cause surprises.&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Unit and integration tests are separated.&lt;/li&gt;
&lt;li&gt;Integration test related utilities can be put next to the integration test and they can be marked as package private, to make sure other tests will not use it.&lt;/li&gt;
&lt;li&gt;Encapsulation will not be broken, if the packaging is correct, both the unit and integration tests will be able to access the allowed classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Maven Compiler Plugin&lt;/strong&gt; must be changed OR a 3rd party Maven plugin, named &lt;strong&gt;build-helper-maven-plugin&lt;/strong&gt; must be introduced to help with the necessary setups.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Surprise option: New maven module
&lt;/h4&gt;

&lt;p&gt;I will not go into details, but I have already seen a few projects where integration tests were not part of the actual Maven module, but they were in a new module which only contained integration tests.&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Unit and integration tests are separated.&lt;/li&gt;
&lt;li&gt;Integration test related utilities can be put next to the integration test and they can be marked as package private, to make sure other tests will not use it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The new maven module must depend on the one it is verifying with the integration tests, so the maven module under test can be built without even verifying its behaviour. You decide if it is an advantage or disadvantage. The whole purpose of doing tests is to make sure an artifact is not being packaged if there are problems with its behaviour.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Wow! That was a long journey, I hope you found it useful and this setup will be utilized in your next project setup 🎉 &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We learnt how Maven works and how it generates and uses its &lt;strong&gt;Effective POM&lt;/strong&gt; to build up its lifecycles.&lt;/li&gt;
&lt;li&gt;We learnt how &lt;strong&gt;Surefire&lt;/strong&gt; and &lt;strong&gt;Failsafe&lt;/strong&gt; plugins work and how they differ.&lt;/li&gt;
&lt;li&gt;We learnt 3 ways to set up our unit and integration tests in detail.&lt;/li&gt;
&lt;li&gt;We learnt a 4th method briefly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm curious what you think about it, if you have made it so far! &lt;/p&gt;

&lt;p&gt;How are you setting up your projects?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you see similarities?&lt;/li&gt;
&lt;li&gt;What are the problems you are facing with your current setup?&lt;/li&gt;
&lt;li&gt;Will you consider separating your unit and integration tests?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; 

&lt;a href="https://github.com/rodnansol/articles-posts/tree/main/Testing-with-Maven" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;Check out the sample project&lt;/a&gt;


&lt;/p&gt;




&lt;h2&gt;
  
  
  Follow Rodnan Sol for more
&lt;/h2&gt;

&lt;p&gt;
&lt;a href="https://twitter.com/rodnansol" rel="noopener noreferrer"&gt;&lt;img alt="Twitter followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Frodnansol%3Flogo%3Dtwitter%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;a href="https://github.com/rodnansol" rel="noopener noreferrer"&gt;&lt;img alt="GitHub followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Ffollowers%2Frodnansol%3Flabel%3DFollow%2520rodnansol%26logo%3Dgithub%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;a href="https://dcbadge.vercel.app/api/server/USyh6XUjvP" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdcbadge.vercel.app%2Fapi%2Fserver%2FUSyh6XUjvP"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the author too
&lt;/h2&gt;

&lt;p&gt; 
&lt;a href="https://twitter.com/therealhnk" rel="noopener noreferrer"&gt;&lt;img alt="Twitter followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Ftherealhnk%3Flogo%3Dtwitter%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;a href="https://github.com/nandorholozsnyak" rel="noopener noreferrer"&gt;&lt;img alt="GitHub followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Ffollowers%2Fnandorholozsnyak%3Flabel%3DFollow%2520nandorholozsnyak%26logo%3Dgithub%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>java</category>
      <category>maven</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Commit Teller - Your personal story teller for your work</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Sun, 21 May 2023 12:51:12 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/commit-teller-your-personal-story-teller-for-your-work-2aa3</link>
      <guid>https://dev.to/nandorholozsnyak/commit-teller-your-personal-story-teller-for-your-work-2aa3</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;A new tool has been built that is able to tell a story about your project based on the commit history of your branches, pull requests.&lt;/p&gt;

&lt;p&gt;The story can extended along the way and it can be attached to the pull request/issue that is being closed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Category Submission:
&lt;/h3&gt;

&lt;p&gt;Wacky Wildcard&lt;/p&gt;

&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/rodnansol/commit-teller-action" rel="noopener noreferrer"&gt;https://github.com/rodnansol/commit-teller-action&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhy1eawr3jfsws42q6cxn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhy1eawr3jfsws42q6cxn.png" alt="Pull request before executing the action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk711vowh0wl82himr6us.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk711vowh0wl82himr6us.png" alt="Successful run"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6a2riwdkfdphem7lxa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6a2riwdkfdphem7lxa6.png" alt="The steps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cewh7szqo4j7zfvj56x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cewh7szqo4j7zfvj56x.png" alt="The result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some other example results, the tool was run on older pull requests, and because of that the author of the comments are myself, and not the github-action bot.&lt;/p&gt;

&lt;p&gt;Smaller ones:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/pull/53#issuecomment-1557572266" rel="noopener noreferrer"&gt;https://github.com/rodnansol/spring-configuration-property-documenter/pull/53#issuecomment-1557572266&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxa5dmu0z1iz2e4yvg94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxa5dmu0z1iz2e4yvg94.png" alt="https://github.com/rodnansol/spring-configuration-property-documenter/pull/53#issuecomment-1557572266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/pull/60#issuecomment-1557576442" rel="noopener noreferrer"&gt;https://github.com/rodnansol/spring-configuration-property-documenter/pull/60#issuecomment-1557576442&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4f19s1p2ww60zfj215wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4f19s1p2ww60zfj215wg.png" alt="https://github.com/rodnansol/spring-configuration-property-documenter/pull/60#issuecomment-1557576442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/pull/45#issuecomment-1557578982" rel="noopener noreferrer"&gt;https://github.com/rodnansol/spring-configuration-property-documenter/pull/45#issuecomment-1557578982&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstecohivsm84taxpf0q6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstecohivsm84taxpf0q6.png" alt="https://github.com/rodnansol/spring-configuration-property-documenter/pull/45#issuecomment-1557578982"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it definitely can do longer ones, in this case the configuration parameters were the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMMIT_TELLER_OPENAI_TEMPERATURE=0.8
COMMIT_TELLER_OPENAI_MAX_TOKEN=2048

COMMIT_TELLER_STORY_TEMPLATE='Write me a story about the following commits. \n
  The story can be only 1536 characters long. \n
  The story should be based on the commit messages, and it should have the following characteristics:'
COMMIT_TELLER_STORY_CHARACTERISTICS='documentation like, funny'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/pull/53#issuecomment-1557591882" rel="noopener noreferrer"&gt;https://github.com/rodnansol/spring-configuration-property-documenter/pull/53#issuecomment-1557591882&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1uyy17lvnsci4ucmxaqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1uyy17lvnsci4ucmxaqe.png" alt="https://github.com/rodnansol/spring-configuration-property-documenter/pull/53#issuecomment-1557591882"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;The GitHub action utilizes a CLI application that is able to read the commit messages for an given pull request and based on it, it can build a story, that can be descriptive, funny, happy, you name the mood of it. The CLI application uses the GitHub API and of course, the nowadays overhyped OpenAI. It connects to OpenAI via their public API (so an API key is needed) and it will translate your commit messages into a beautiful story.&lt;/p&gt;

&lt;p&gt;The CLI application was built with Quarkus, as a Java developer I wanted to use a framework that can help me move fast and I have confidence and fun with that. I definitely did!&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/commit-teller-action" rel="noopener noreferrer"&gt;https://github.com/rodnansol/commit-teller-action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/commit-teller" rel="noopener noreferrer"&gt;https://github.com/rodnansol/commit-teller&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.apache.org/licenses/" rel="noopener noreferrer"&gt;Apache&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background (What made you decide to build this particular app? What inspired you?)
&lt;/h2&gt;

&lt;p&gt;OpenAI's ChatGPT shocked the world (you decide if it happened in a positive or in a negative way for you) and developers, companies are already utilizing their features and enhancing their productivity. There are people who are creating content with that, and I thought... Wait a minute, how about an app that can integrate OpenAI's API and it can summarize me commit messages for pull request. There are million projects out there, and you might not know its story, maybe you want to dig deeper and get to know every small detail of it, so how about a tool that can help your with that?&lt;/p&gt;

&lt;p&gt;Don't you like writing documentation? Then write detailed commit messages and use the tool for it! It will not be a full-blown documentation for your API, but if you are just playing with your skills and thoughts this one will help your to have visibility for other people if they are interested in your work.&lt;/p&gt;

&lt;p&gt;The characteristics, the OpenAI model and many more can be configured, so you have the ability to have your own story behind your commits :) 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  How I built it (How did you utilize GitHub Actions or GitHub Codespaces? Did you learn something new along the way? Pick up a new skill?)
&lt;/h3&gt;

&lt;p&gt;As a &lt;a href="https://quarkus.io/" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt; fan, I'm using this Java framework whenever I can, and for CLI apps this is my go to choice. &lt;/p&gt;

&lt;h4&gt;
  
  
  But why a CLI app?
&lt;/h4&gt;

&lt;p&gt;Because with that I can use it not just in a GitHub action but on my laptop as well, but it definitely gives us wacky questions, why would you like to run it on your laptop? 🧐&lt;/p&gt;

&lt;p&gt;First things first, I just made the new Quarkus project, from the template that I have been already setup for CLI applications in my brand new organization called &lt;a href="https://github.com/rodnansol" rel="noopener noreferrer"&gt;Rodnan Sol&lt;/a&gt; on GitHub (Do not forget to follow me here 🧑‍💻!)&lt;/p&gt;

&lt;p&gt;After the CLI app was ready I made a new repository which contains the setup/configuration for the GitHub action as well.&lt;/p&gt;

&lt;p&gt;The setup was easy as the app requires a GitHub token, but GitHub actions always have one implicitly setup for you, but their permission can be changes, this is something I learnt during this time I was making the action setup. This is cool because the token can be narrowed down to the least required list of permissions, so if your action does not have manage repositories you can definitely turn those off, and if they get leaked during the action's lifetime, it could cause no harm to you and your repositories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources/Info
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A series about the implementation, from the idea to the usage: &lt;a href="https://dev.to/nandorholozsnyak/commit-teller-the-beginning-2l5"&gt;https://dev.to/nandorholozsnyak/commit-teller-the-beginning-2l5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Example GitHub repository: &lt;a href="https://github.com/rodnansol/commit-teller-example" rel="noopener noreferrer"&gt;https://github.com/rodnansol/commit-teller-example&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Action itself: &lt;a href="https://github.com/rodnansol/commit-teller-action" rel="noopener noreferrer"&gt;https://github.com/rodnansol/commit-teller-action&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The CLI application: &lt;a href="https://github.com/rodnansol/commit-teller" rel="noopener noreferrer"&gt;https://github.com/rodnansol/commit-teller&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rodnan Sol GitHub pages: &lt;a href="https://rodnansol.github.io/commit-teller/latest/index.html" rel="noopener noreferrer"&gt;https://rodnansol.github.io/commit-teller/latest/index.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href="https://twitter.com/TheRealHNK" rel="noopener noreferrer"&gt;https://twitter.com/TheRealHNK&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rodnan Sol Twitter: &lt;a href="https://twitter.com/rodnansol" rel="noopener noreferrer"&gt;https://twitter.com/rodnansol&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>githubhack23</category>
      <category>github</category>
      <category>quarkus</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Commit Teller - How to use it?</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Mon, 15 May 2023 19:18:17 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/commit-teller-how-to-use-it-2c08</link>
      <guid>https://dev.to/nandorholozsnyak/commit-teller-how-to-use-it-2c08</guid>
      <description>&lt;h2&gt;
  
  
  "Well... We are waiting"
&lt;/h2&gt;

&lt;p&gt;The tool is almost ready, it is time to show of what it can do.&lt;/p&gt;

&lt;p&gt;Let's wire it into one of our repositories, start coding and let's open a pull request. When everything is ready, merge your results and let the GitHub action run 🔥&lt;/p&gt;

&lt;p&gt;How does our pull request look before the tool is executed?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xnDWY7Bq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65t5yxkvawfeqsnps68m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xnDWY7Bq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65t5yxkvawfeqsnps68m.png" alt="Before Story" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's merge it! 🔥&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--toZXNvWg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/63kilccqyd7qrsgbbfay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--toZXNvWg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/63kilccqyd7qrsgbbfay.png" alt="Merge time" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's check the GitHub Action itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_-e0VVg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3yjw8251b4ew01lm5ipl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_-e0VVg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3yjw8251b4ew01lm5ipl.png" alt="GitHub Action" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, the result itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rQe7Spx---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z8favs53k15uzjtss3um.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rQe7Spx---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z8favs53k15uzjtss3um.png" alt="Comment Result" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to wire it into my repository?
&lt;/h2&gt;

&lt;p&gt;Let's look at how it could be wired into your repository.&lt;/p&gt;

&lt;p&gt;The setup is easy, because it is also "just" a GitHub Action, and if you have already setup one you will know what it looks like.&lt;/p&gt;

&lt;p&gt;After you created your repository clone it (or you can do it online too) and create a folder named &lt;code&gt;.github&lt;/code&gt; into the directory. Inside that directory create another one called &lt;code&gt;workflows&lt;/code&gt; and in that create a &lt;code&gt;create-pull-request-story.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Open the &lt;strong&gt;yaml&lt;/strong&gt; file and use the following as a template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create story about Pull Request&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;closed&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create-story-for-pull-request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create story for pull request at merge&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event.pull_request.merged == &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rodnansol/commit-teller-action@v0.1.0&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create story&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ISSUE_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.number }}&lt;/span&gt;
          &lt;span class="na"&gt;COMMIT_TELLER_GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;COMMIT_TELLER_OPENAI_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.OPENAI_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;early-access&lt;/span&gt;
          &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create-story -pr ${ISSUE_NUMBER}&lt;/span&gt;
           &lt;span class="s"&gt;--owner ${GITHUB_REPOSITORY%/*}&lt;/span&gt;
           &lt;span class="s"&gt;--repository ${GITHUB_REPOSITORY#*/}&lt;/span&gt;
           &lt;span class="s"&gt;-saf=true --file-extension ADOC&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit story changes&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ISSUE_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.number }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;mkdir -p docs&lt;/span&gt;
          &lt;span class="s"&gt;mv *.adoc docs&lt;/span&gt;
          &lt;span class="s"&gt;git add *.adoc&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.email "${{ secrets.COMMITTER_EMAIL }}"&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.name "${{ secrets.COMMITTER_NAME }}"&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "Creating story about pull request #${ISSUE_NUMBER}"&lt;/span&gt;
          &lt;span class="s"&gt;git push origin main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the action to run when a pull request is closed, it starts on the second line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;closed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that &lt;code&gt;if&lt;/code&gt; you can specify when to really run the GitHub action, as pull request can be done without merging the branch into the specified target, but if you want to run the action only the code is merged specify the following (as seen before):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event.pull_request.merged == &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the action with any available environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMMIT_TELLER_GITHUB_TOKEN=&amp;lt;github-token&amp;gt; #1
COMMIT_TELLER_OPENAI_API_KEY=&amp;lt;openai-token&amp;gt; #2

COMMIT_TELLER_OPENAI_MODEL=text-davinci-003 #3
COMMIT_TELLER_OPENAI_TEMPERATURE=0.3 #4
COMMIT_TELLER_OPENAI_MAX_TOKEN=512 #5

COMMIT_TELLER_STORY_TEMPLATE='Write me a story about the following commits. \n
  The story can be only 512 characters long. \n
  The story should be based on the commit messages, and it should have the following characteristics:' #6
COMMIT_TELLER_STORY_CHARACTERISTICS='documentation like, funny' #7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1 - Create a GitHub token here, for GitHub actions it is optional, because the action is always having a token.&lt;br&gt;
2 - Create an OpenAI token here: &lt;a href="https://platform.openai.com/account/api-keys"&gt;https://platform.openai.com/account/api-keys&lt;/a&gt;&lt;br&gt;
3 - OpenAI’s models: &lt;a href="https://platform.openai.com/docs/models/model-endpoint-compatibility"&gt;https://platform.openai.com/docs/models/model-endpoint-compatibility&lt;/a&gt;&lt;br&gt;
4 - Temperature value. More about it here: &lt;a href="https://platform.openai.com/docs/api-reference/completions/create#completions/create-temperature"&gt;https://platform.openai.com/docs/api-reference/completions/create#completions/create-temperature&lt;/a&gt;&lt;br&gt;
5 - Number of the maximum tokens for the completion response.&lt;br&gt;
6 - The message to be passed to the language processor. It will include the commit messages as a list.&lt;br&gt;
7 - Define the characteristics of the story.&lt;/p&gt;

&lt;p&gt;For the official documentation please check the following website: &lt;a href="https://rodnansol.github.io/commit-teller/latest/index.html"&gt;https://rodnansol.github.io/commit-teller/latest/index.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Commit stories to the repository
&lt;/h2&gt;

&lt;p&gt;By default a comment will be made to the associated pull request, but if you want to collect the stories at one place you generate the result to a file and commit to the repository with the final step. 🔥&lt;/p&gt;

</description>
      <category>githubhack23</category>
      <category>githubactions</category>
      <category>quarks</category>
      <category>programming</category>
    </item>
    <item>
      <title>Commit Teller - Problems during the development</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Sat, 13 May 2023 14:05:09 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/commit-teller-problems-during-the-development-3jm2</link>
      <guid>https://dev.to/nandorholozsnyak/commit-teller-problems-during-the-development-3jm2</guid>
      <description>&lt;h2&gt;
  
  
  🚨 Problems
&lt;/h2&gt;

&lt;p&gt;After the start I knew that I'll be integrating OpenAI's API and the GitHub API into the application, and I wanted to make sure I can go native with Quarkus. Going native requires GraalVM, but it is not enough, your code must be compatible with the native generation, and at after the first dependencies I pulled in I was not checking it frequently if my code is still able to be compiled into native.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔨 Dependencies
&lt;/h2&gt;

&lt;p&gt;I only needed two important dependencies that can fulfil my requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/jcabi/jcabi-github"&gt;jcabi-github&lt;/a&gt; - The lib to access the GitHub API easily&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/TheoKanning/openai-java"&gt;openai-java&lt;/a&gt; - The lib to access the OpenAI's API easily&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Why did I want to use 3rd party libs?
&lt;/h2&gt;

&lt;p&gt;I wanted to move quickly, I checked the GitHub's API, it looked fine, but the request and response objects can be really big, so if there is any lib that can help me to skip writing a lot of boilerplate code, I try to utilize them.&lt;/p&gt;

&lt;p&gt;OpenAI integration is not different, I wanted to skip writing the boilerplate and move fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧐 Problems and solution with the OpenAI API
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/TheoKanning/openai-java"&gt;openai-java&lt;/a&gt; has 3 modules that the developers can use, and I needed all of them to fulfil the requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;api&lt;/li&gt;
&lt;li&gt;client&lt;/li&gt;
&lt;li&gt;server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The usage of the lib was easy, multiple features are implemented but for me the &lt;em&gt;prompt completion&lt;/em&gt; was the needed one. It has small request and response objects, with a client class that can invoke the API.&lt;/p&gt;

&lt;p&gt;The problems arrived when I tried to compile it to native, and in the background the lib uses the Retrofit library, which in my opinion created the problem. I tried to dig deeper, but a few hours just passed, and I said that is enough, I know Quarkus has an &lt;a href="https://quarkus.io/guides/rest-client-reactive"&gt;extension&lt;/a&gt; to invoke REST APIs, I can do it super fast by just writing the request and response classes.&lt;/p&gt;

&lt;p&gt;The client interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RegisterRestClient&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;OpenAIClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/v1/completions"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@ClientHeaderParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer {token}"&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;CompletionResponse&lt;/span&gt; &lt;span class="nf"&gt;postCompletion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;CompletionRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="nd"&gt;@NotBody&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;CompletionRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"max_tokens"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxTokens&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the response class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;CompletionResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Completion&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Completion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the configuration for the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;quarkus.rest-client."org.rodnansol&lt;/span&gt;
&lt;span class="err"&gt;.committeller.core.language&lt;/span&gt;
&lt;span class="err"&gt;.openai.OpenAIClient"&lt;/span&gt;
&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://api.openai.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, and poor me was looking for libs that can help me with that :) It took less than 10 minutes to write it and wire it into the existing code. &lt;/p&gt;

&lt;p&gt;I used records, because these classes are just data classes, I'm not going to manipulate them later, just use the results.&lt;/p&gt;

&lt;p&gt;The point of execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpenAILanguageProcessor&lt;/span&gt;
    &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;LanguageProcessor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="no"&gt;LOGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OpenAILanguageProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OpenAIProperties&lt;/span&gt; &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OpenAIClient&lt;/span&gt; &lt;span class="n"&gt;openAIClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OpenAILanguageProcessor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;OpenAIProperties&lt;/span&gt; &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@RestClient&lt;/span&gt; &lt;span class="nc"&gt;OpenAIClient&lt;/span&gt; &lt;span class="n"&gt;openAIClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openAIProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openAIClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openAIClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ProcessResult&lt;/span&gt; &lt;span class="nf"&gt;processPrompt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;ProcessPromptCommand&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;CompletionResponse&lt;/span&gt; &lt;span class="n"&gt;completionResponse&lt;/span&gt;
            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openAIClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;postCompletion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CompletionRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxToken&lt;/span&gt;&lt;span class="o"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;openAIProperties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CompletionResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Completion&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;
            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;completionResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LanguageProcessorException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"No choices are returned,"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                    &lt;span class="s"&gt;" unable to return result"&lt;/span&gt;
            &lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;completionChoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProcessResult&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completionChoice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, native was working fine. 🔥&lt;/p&gt;

&lt;h2&gt;
  
  
  🙈 Problems and (partial) solution with the GitHub API
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/jcabi/jcabi-github"&gt;jcabi-github&lt;/a&gt; dependency looked nice in the beginning but it was having issues with the event feed management, the endpoints were not properly setup in the code, but it was only a minor issue, I was able to overcome it by using two different operations to fetch the required data. &lt;/p&gt;

&lt;p&gt;After trying to go native, the problems just came out, the lib is written in a way that the native compiler was unable to overcome and I was looking for quick fixes. In Quarkus you can setup hints for native compiler, but it was not clear at first glance, what should be setup.&lt;/p&gt;

&lt;p&gt;The solution was to move to another solution and use a Quarkus &lt;a href="https://github.com/quarkiverse/quarkus-github-api"&gt;extension&lt;/a&gt; that I did not discover at the first place.&lt;/p&gt;

&lt;p&gt;Quarkus extensions are basically plugins for the framework itself, you need/should/would write an extension when you want to utilize the framework benefits like build-time optimizations or the native binary generation. I was happy that I found it because if you find an official Quarkus extension than you can "hope" that it will compile to native.&lt;/p&gt;

&lt;p&gt;Not in this case 🚧&lt;/p&gt;

&lt;p&gt;The extension is backed by a different 3rd party lib that can interact with the GitHub API, it is called &lt;a href="https://github.com/hub4j/github-api"&gt;Hub4j GitHub API&lt;/a&gt;, the change from the jcabi dependency was easy and everything was working as before, BUT not the native mode.&lt;/p&gt;

&lt;p&gt;There are some problems with that dependency too, I was digging deeper but I was unable to figure out the real reason behind that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outcome
&lt;/h2&gt;

&lt;p&gt;Even I planned to go native but I can not, this does not mean the tool can not be used, it definitely can be, but users would need Java on their machine to run it, but the GitHub action will contain a Java 17 so there should be no problem with the execution. 🎉&lt;/p&gt;

</description>
      <category>quarkus</category>
      <category>githubhack23</category>
      <category>github</category>
      <category>programming</category>
    </item>
    <item>
      <title>Commit Teller - The implementation</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Thu, 04 May 2023 20:06:25 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/commit-teller-the-implementation-kam</link>
      <guid>https://dev.to/nandorholozsnyak/commit-teller-the-implementation-kam</guid>
      <description>&lt;h2&gt;
  
  
  How will it be implemented?
&lt;/h2&gt;

&lt;p&gt;As a Java developer I wanted to stick to Java, and I love the &lt;a href="https://quarkus.io/"&gt;Quarkus&lt;/a&gt; project, so I thought I will create a CLI application that can do the job, a CLI app that can be the backend of the GitHub action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration points
&lt;/h2&gt;

&lt;p&gt;In the CLI application I need two integration points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One to the GitHub API - It can return me the issues, pull requests and it can create me comments on any of them.&lt;/li&gt;
&lt;li&gt;The other to the OpenAI, this is basically the brain of the stories, this will be able to parse, and interpret your commit messages and create the story about that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;The project will contain 2 modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One will be the core&lt;/li&gt;
&lt;li&gt;And the other one will be the CLI module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm just used to these setup, because if the core module contains the core "business" logic I can easily use it for other modules as well, like for a &lt;a href="https://www.jbang.dev/"&gt;JBang&lt;/a&gt; script or for a &lt;a href="https://maven.apache.org/"&gt;Maven&lt;/a&gt; plugin easily.&lt;/p&gt;

&lt;p&gt;With this setup I can "practice" the setup of multi-module projects with Quarkus. While it is probably super straightforward for other frameworks or pure Java projects, in case of Quarkus, if the developer would like to use the super power of the framework, the &lt;a href="https://quarkus.io/guides/dev-mode-differences"&gt;developer mode&lt;/a&gt; or the &lt;a href="https://quarkus.io/blog/quarkus-2-0-0-alpha2-released/"&gt;continuous testing&lt;/a&gt; then the core module must include the &lt;a href="https://quarkus.io/guides/maven-tooling#multi-module-maven"&gt;Jandex&lt;/a&gt; Maven plugin to make sure the mentioned stuff is working well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uber-JAR
&lt;/h2&gt;

&lt;p&gt;I'm planning to bundle the CLI application as an &lt;a href="https://imagej.net/develop/uber-jars"&gt;Uber-JAR&lt;/a&gt; that will contain all the necessary dependencies for the application and anybody with Java on their system will be able to run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How about compiling it to native?
&lt;/h2&gt;

&lt;p&gt;A few weeks ago I started &lt;a href="https://github.com/rodnansol/jbang-catalog-document-generator"&gt;another project&lt;/a&gt; with Quarkus and I was surprised that the native compilation is getting faster and faster, I do not say it is like lightning fast, but I was shocked that a CLI app with two modules and with a few dependencies was compiled in less than 2 minutes to native mode.&lt;/p&gt;

&lt;p&gt;In 2019 when Quarkus was released with its GraalVM integration it was not that easy to setup the whole environment, but today the Quarkus plugins (Maven or Gradle) are super smart and even if there is no GraalVM (or Mandrel) installed on the machine, if it detects Docker it can compile it to native, and on the other hand Quarkus devs also recommend to use the Docker way, because if it is being built on your local machine, you might misses some crucial dependencies, and it may run on your system but not on others.&lt;/p&gt;

&lt;p&gt;We will see if we can compile it to native and host it as a Docker image.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ready from the implementation?
&lt;/h2&gt;

&lt;p&gt;Almost nothing, only a small PoC that can connect to the GitHub API and to the OpenAI's API.&lt;/p&gt;

&lt;p&gt;Stay tuned for more and leave feedback about the project idea. &lt;/p&gt;

</description>
      <category>githubhack23</category>
      <category>java</category>
      <category>quarkus</category>
      <category>programming</category>
    </item>
    <item>
      <title>Commit Teller - The beginning</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Thu, 27 Apr 2023 18:10:52 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/commit-teller-the-beginning-2l5</link>
      <guid>https://dev.to/nandorholozsnyak/commit-teller-the-beginning-2l5</guid>
      <description>&lt;h2&gt;
  
  
  Another tool for my daily job? But why?
&lt;/h2&gt;

&lt;p&gt;The idea for Commit Teller came after I read the official announcement about the &lt;strong&gt;GitHubHack23&lt;/strong&gt; event, and started to think about a cool stuff that can utilize GitHub actions.&lt;/p&gt;

&lt;p&gt;I use GitHub actions almost on a daily basis for my personal projects and for my projects under the &lt;a href="https://github.com/rodnansol"&gt;Rodnan Sol&lt;/a&gt; organization. It is utilized to execute CI builds and to create releases for our applications. I used to use Bitbucket and GitLab and their CI solutions, GitHub Actions are not different, but they are not the same, the concept is similar, but the syntax and the usages are different.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Commit Teller?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Have you ever wondered about, when you opened an issue or a pull request in an open source project on GitHub, what could be story behind this issue or pull request? If it was a feature request issue/ticket, why did the author asked for it or if it was a pull request, what was the story of the work? - How did the author started to work on it?&lt;/li&gt;
&lt;li&gt;How many times did they refactor their initial concept?&lt;/li&gt;
&lt;li&gt;Can we utilize a tool that can create us documentation about our GitHub issues and/or pull requests (its commits and comments)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Commit Teller&lt;/strong&gt; is here to answer these questions. &lt;/p&gt;

&lt;h2&gt;
  
  
  The concept
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Commit Teller&lt;/strong&gt; reads your issue and pull request comments and commits after a Pull Request is being merged, and it creates a story about them that is being written to the issue or pull request. You can definitely save that story and you can commit under into your repository to make sure you can create a bigger story from that. Think about it if you could relive the story of the &lt;a href="https://spring.io/"&gt;Spring framework&lt;/a&gt; or the story of the famous 3rd party libs you use on a daily basis to read CSV files, make database connections.&lt;/p&gt;

&lt;p&gt;Not all open source projects are having well written documentations, and sometimes developers have to dig deeper into the source code itself to find answers, it would be great to rely on well written commits and create documentation from them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core
&lt;/h2&gt;

&lt;p&gt;We are living in an age of the AI tools, this app is not going to be different, it will utilize OpenAI's functionalities to make sure the stories can be built around the comments and commits for the different issues and/or pull requests.&lt;/p&gt;

&lt;p&gt;After your pull requests gets merged a preconfigured GitHub action will be triggered, that will utilize OpenAI's capabilities to generate the story for your pull request and the tool itself will send the generated story to the pull request. &lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;Yes, if you are thinking how the name came, I just thought about to use the name &lt;strong&gt;Story Teller&lt;/strong&gt; but as developers we are making more commits on a daily basis than stories for sure.&lt;/p&gt;

&lt;p&gt;Stay tuned for more news on the progress for &lt;strong&gt;Commit Teller&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>githubhack23</category>
      <category>java</category>
      <category>github</category>
      <category>githubaction</category>
    </item>
    <item>
      <title>Spring Configuration Property Documenter</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Fri, 10 Mar 2023 10:50:00 +0000</pubDate>
      <link>https://dev.to/rodnan-sol/spring-configuration-property-documenter-3d9p</link>
      <guid>https://dev.to/rodnan-sol/spring-configuration-property-documenter-3d9p</guid>
      <description>&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Have you ever felt that you have no proper way to oversee a project because it is having a lot of customizable properties, but they are not documented, and you do not know where to start a new feature or bugfix?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have you ever felt like you are having too much services, and you do not know how to customize them externally?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have you ever wondered is there any way to document your application's properties that you are having in your project?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If for any question your answer is &lt;strong&gt;YES&lt;/strong&gt; then you are at the best place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Configuration Property Documenter
&lt;/h2&gt;

&lt;p&gt;This tool lets you generate informative and useful documentations about the classes annotated with the &lt;code&gt;@ConfigurationProperties&lt;/code&gt; in your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  But how?
&lt;/h3&gt;

&lt;p&gt;It is easy, if you are working with Spring Boot, you first need a dependency on your classpath during build time, it is annotation processor that will read your classes and generates a metadata file based on the annotation configuration classes.&lt;/p&gt;

&lt;p&gt;In case of Maven, you should add the following dependency:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-configuration-processor&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;optional&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/optional&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After a &lt;code&gt;mvn package&lt;/code&gt; the following file should appear in your &lt;code&gt;target&lt;/code&gt; folder's &lt;code&gt;META-INF&lt;/code&gt; folder: &lt;code&gt;spring-configuration-metadata.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is a simple JSON file, but it contains information about your configuration properties.&lt;/p&gt;

&lt;p&gt;Let's see an example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"this.is.my"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyProperties&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This is my variable.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Deprecated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Since you are a pilot"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;anotherVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"with default value"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * A duration.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofDays&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;instant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofEpochSecond&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1995&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;dateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1995&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Getters &amp;amp; Setters&lt;/span&gt;

    &lt;span class="nd"&gt;@DeprecatedConfigurationProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Because it is deprecated"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"instant"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="nf"&gt;getDuration&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Generated &lt;code&gt;spring-configuration-metadata.json&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"groups"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my.date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java.time.LocalDate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my.date-time"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java.time.LocalDateTime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my.instant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java.time.Instant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my.variable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java.lang.String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is my variable."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my.another-variable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java.lang.String"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"deprecated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"deprecation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"this.is.my.duration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"java.time.Duration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A duration."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org.rodnansol.MyProperties"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"deprecated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"deprecation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Because it is deprecated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"replacement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"instant"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Okay, but what's next?
&lt;/h3&gt;

&lt;p&gt;We built you a tool that can use this JSON file and can generate a documentation from it in the following formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;AsciiDoc&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;XML - in case of an XSLT transformation and with XSD support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You name it&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find it &lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  What can it do?
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Reading one or multiple metadata files
&lt;/h4&gt;

&lt;p&gt;After it reads your metadata JSON file, a document will be created in the specified format.&lt;/p&gt;

&lt;p&gt;Multiple JSON metadata files can be specified before the process, multi-module projects can also utilize it to have only one document where all the properties are available from the different modules.&lt;/p&gt;

&lt;p&gt;If the metadata file is in a JAR file, it is not a problem, the tool can read it from the JAR file.&lt;/p&gt;
&lt;h4&gt;
  
  
  Aggregating more metadata files
&lt;/h4&gt;

&lt;p&gt;If you are working in a multi-module environment, you may have multiple metadata files, but you do not have to worry, single documentations and aggregated documentations also can be produced.&lt;/p&gt;
&lt;h4&gt;
  
  
  Default templates and customization
&lt;/h4&gt;

&lt;p&gt;The tool provides you a default template that is written with &lt;strong&gt;Handlebars&lt;/strong&gt;, but it is also prepared to support custom templates as well.&lt;/p&gt;

&lt;p&gt;By default, the &lt;strong&gt;Handlebars&lt;/strong&gt; templating engine is supported, but you have the ability to use your own templating engine, but you have to write an extension for it.&lt;/p&gt;

&lt;p&gt;Check out the documentation &lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/blob/master/docs/template-customization.adoc#custom-template-compiler" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Excluding and including groups and properties
&lt;/h4&gt;

&lt;p&gt;As the tool lets you read and use metadata files from JAR files as well, you are able to extract information from the official Spring Boot JARs and you can put this into your final documenatation.&lt;/p&gt;

&lt;p&gt;Sometimes these files are big and you may want to select specific groups and/or keys to be rendered or not rendered into the final output.&lt;/p&gt;

&lt;p&gt;Exclusion and inclusion lists are also supported.&lt;/p&gt;

&lt;p&gt;Please check the following samples &lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/blob/master/samples/multi-module/multi-module-docs/pom.xml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to use the tool?
&lt;/h3&gt;

&lt;p&gt;The core functionalities are built with Java 11 and these functionalities are available through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/blob/master/docs/maven-plugin.adoc" rel="noopener noreferrer"&gt;Maven plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/blob/master/docs/usage-jbang.adoc" rel="noopener noreferrer"&gt;JBang script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/blob/master/docs/gradle-plugin.adoc" rel="noopener noreferrer"&gt;Gradle plugin (Coming soon)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Maven usage
&lt;/h3&gt;

&lt;p&gt;Just add the following plugin to your &lt;code&gt;pom.xml&lt;/code&gt; and you are ready to go.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;


&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.rodnansol&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-configuration-property-documenter-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${final-version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;generate-adoc&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;process-classes&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;generate-property-document&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;ADOC&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fmaven-central%2Fv%2Forg.rodnansol%2Fspring-configuration-property-documenter%3Flabel%3DThe%2520final%2520version%2520is%2520%26style%3Dfor-the-badge" class="article-body-image-wrapper"&gt;&lt;img alt="Final version" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fmaven-central%2Fv%2Forg.rodnansol%2Fspring-configuration-property-documenter%3Flabel%3DThe%2520final%2520version%2520is%2520%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Check the available Maven goals &lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/blob/master/docs/maven-plugin.adoc#available-goals-and-config" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Template customization
&lt;/h3&gt;

&lt;p&gt;The templates can be customized if the default one is not fitting your requirements, please read the docs about that &lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/tree/master/samples" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1cobryz1gzvkmfnu27b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1cobryz1gzvkmfnu27b.png" alt="AsciiDoc Example"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft11190vbiz0bnehyeeav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft11190vbiz0bnehyeeav.png" alt="AsciiDoc Example 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Checkout the samples &amp;amp; docs
&lt;/h3&gt;

&lt;p&gt;Feel free to check out the samples and documentation because it will give a wider knowledge about the how-tos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/tree/master/samples" rel="noopener noreferrer"&gt;Samples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/tree/master/docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Are you using Spring Boot with Quarkus?
&lt;/h3&gt;

&lt;p&gt;If yes, do not worry, we got you covered, check out this &lt;a href="https://github.com/rodnansol/spring-configuration-property-documenter/tree/master/samples/quarkus-spring-boot-single-module" rel="noopener noreferrer"&gt;sample&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Rodnan Sol for more
&lt;/h2&gt;

&lt;p&gt;
&lt;a href="https://twitter.com/rodnansol" rel="noopener noreferrer"&gt;&lt;img alt="Twitter followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Frodnansol%3Flogo%3Dtwitter%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;a href="https://github.com/rodnansol" rel="noopener noreferrer"&gt;&lt;img alt="GitHub followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Ffollowers%2Frodnansol%3Flabel%3DFollow%2520rodnansol%26logo%3Dgithub%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;a href="https://dcbadge.vercel.app/api/server/USyh6XUjvP" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdcbadge.vercel.app%2Fapi%2Fserver%2FUSyh6XUjvP"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the author too
&lt;/h2&gt;

&lt;p&gt; 
&lt;a href="https://twitter.com/therealhnk" rel="noopener noreferrer"&gt;&lt;img alt="Twitter followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Ftherealhnk%3Flogo%3Dtwitter%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;a href="https://github.com/nandorholozsnyak" rel="noopener noreferrer"&gt;&lt;img alt="GitHub followers" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Ffollowers%2Fnandorholozsnyak%3Flabel%3DFollow%2520nandorholozsnyak%26logo%3Dgithub%26style%3Dfor-the-badge"&gt;&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>asciidoc</category>
      <category>html</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Local development "environments" per Git branches with Docker Compose</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Tue, 08 Nov 2022 14:20:06 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/local-development-environments-per-git-branches-with-docker-compose-2k6o</link>
      <guid>https://dev.to/nandorholozsnyak/local-development-environments-per-git-branches-with-docker-compose-2k6o</guid>
      <description>&lt;h2&gt;
  
  
  What are Git Hooks?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.search.brave.com%2Fbgl_GA9iLqrQJ8EKiwiutgnDZHspbXJo621bn_BTpVI%2Frs%3Afit%3A910%3A380%3A1%2Fg%3Ace%2FaHR0cHM6Ly9naXQt%2Fc2NtLmNvbS9pbWFn%2FZXMvbG9nb3MvZG93%2FbmxvYWRzL0dpdC1M%2Fb2dvLTJDb2xvci5w%2Fbmc" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.search.brave.com%2Fbgl_GA9iLqrQJ8EKiwiutgnDZHspbXJo621bn_BTpVI%2Frs%3Afit%3A910%3A380%3A1%2Fg%3Ace%2FaHR0cHM6Ly9naXQt%2Fc2NtLmNvbS9pbWFn%2FZXMvbG9nb3MvZG93%2FbmxvYWRzL0dpdC1M%2Fb2dvLTJDb2xvci5w%2Fbmc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever wondered what are the Git hooks and how can they help you be more "effective &amp;amp; productive" ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" rel="noopener noreferrer"&gt;Git Hooks&lt;/a&gt; are basically "scripts" that can run when specific events are being triggered, like you are making a commit, or you are receiving commits from the remote. There are two types of hooks, &lt;strong&gt;client-side&lt;/strong&gt; and &lt;strong&gt;server-side&lt;/strong&gt;. In this blog post we are going to discover one of the &lt;strong&gt;client-side&lt;/strong&gt; hooks named &lt;code&gt;post-checkout&lt;/code&gt;. These hooks must be present in the &lt;code&gt;.git/hooks&lt;/code&gt; folder or your project, but it can be changed with the &lt;code&gt;git config core.hooksPath&lt;/code&gt; command. If you are interested in the basics please check out it this page: &lt;a href="https://git-scm.com/docs/githooks" rel="noopener noreferrer"&gt;https://git-scm.com/docs/githooks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are projects where the commit messages are being validated via a specific hook to make sure the messages will be having the "conventional commit" style. &lt;/p&gt;

&lt;p&gt;Javascript developers are probably familiar with a tool called &lt;a href="https://typicode.github.io/husky/#/?id=create-a-hook" rel="noopener noreferrer"&gt;Husky&lt;/a&gt; it lets you create Git hooks easily installed for the developers. In this blog post we are going to use a Maven project with a Maven plugin that will automatically install hooks into your &lt;code&gt;.git&lt;/code&gt; repository. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Docker Compose?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.search.brave.com%2FCztuz68opFn9G3h9kS4TBXV6kUgFWPlQrNkZfmm6_Cs%2Frs%3Afit%3A1200%3A1200%3A1%2Fg%3Ace%2FaHR0cDovL2xvZ29z%2FLWRvd25sb2FkLmNv%2FbS93cC1jb250ZW50%2FL3VwbG9hZHMvMjAx%2FNi8wOS9Eb2NrZXJf%2FbG9nby5wbmc" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.search.brave.com%2FCztuz68opFn9G3h9kS4TBXV6kUgFWPlQrNkZfmm6_Cs%2Frs%3Afit%3A1200%3A1200%3A1%2Fg%3Ace%2FaHR0cDovL2xvZ29z%2FLWRvd25sb2FkLmNv%2FbS93cC1jb250ZW50%2FL3VwbG9hZHMvMjAx%2FNi8wOS9Eb2NrZXJf%2FbG9nby5wbmc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the help of the &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; we are able to create so called YAML files, to create and manage containers "quickly". We can commit these files to our repositories and other developers are able to set up their dependencies easily when they join a project. When I talk about dependencies I mean databases, messaging services, other services (like Spring based apps that are being containerized).  &lt;/p&gt;

&lt;p&gt;For this &lt;a href="https://docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and of course the &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; executables must be installed.&lt;/p&gt;

&lt;p&gt;An example with Postgres database - the file named by default must be: &lt;strong&gt;docker-compose.yml&lt;/strong&gt; and in the directory the following command must be invoked to start the Postgres container: &lt;code&gt;docker-compose up&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;postgres-db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:14&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-password&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After starting the container (&lt;code&gt;docker-compose up&lt;/code&gt;) similar logs should appear:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Creating docker-env-with-git-hooks_postgres-db_1 ... done
Attaching to docker-env-with-git-hooks_postgres-db_1
postgres-db_1  | The files belonging to this database system will be owned by user "postgres".
postgres-db_1  | This user must also own the server process.
postgres-db_1  | 
postgres-db_1  | The database cluster will be initialized with locale "en_US.utf8".
postgres-db_1  | The default database encoding has accordingly been set to "UTF8".
postgres-db_1  | The default text search configuration will be set to "english".
postgres-db_1  | 
postgres-db_1  | Data page checksums are disabled.
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In another terminal if we execute the &lt;code&gt;docker ps&lt;/code&gt; command we should see similar output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS           PORTS                                    NAMES
17adc0b1b905   postgres:14  &lt;span class="s2"&gt;"docker-entrypoint.s…"&lt;/span&gt;   13 minutes ago   Up 13 minutes   0.0.0.0:5432-&amp;gt;5432/tcp, :::5432-&amp;gt;5432/tcp docker-env-with-git-hooks_postgres-db_1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For me the container ID is &lt;strong&gt;17adc0b1b905&lt;/strong&gt; but on your machine it is probably different, most of the columns are self-explanatory but right now I would like to focus on the &lt;strong&gt;NAMES&lt;/strong&gt; column, and the name for our container is: &lt;strong&gt;docker-env-with-git-hooks_postgres-db_1&lt;/strong&gt;. We did not specify any names, so it needs some explanation.&lt;/p&gt;

&lt;p&gt;Let's break down the name: &lt;strong&gt;docker-env-with-git-hooks_postgres-db_1&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;docker-env-with-git-hooks&lt;/strong&gt; - in my case it is the name of the folder where the &lt;strong&gt;docker-compose.yml&lt;/strong&gt; is - this can be overwritten with a flag when the &lt;code&gt;docker-compose&lt;/code&gt; is being called&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;postgres-db&lt;/strong&gt; - this is the name of the &lt;strong&gt;service&lt;/strong&gt; we specified in the &lt;strong&gt;docker-compose.yml&lt;/strong&gt; file - this can be overwritten with a &lt;code&gt;container_name&lt;/code&gt; attribute in the &lt;strong&gt;docker-compose.yml&lt;/strong&gt; file, but it will overwrite the whole name, nothing else will alternate it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; - this is replica number of the container, we can scale containers and in those cases their postfix numbers are changing - if we specify the &lt;code&gt;container_name&lt;/code&gt; attribute for the specific service the number postfix will be gone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To stop the running container just press &lt;code&gt;Ctrl+C&lt;/code&gt; in the terminal window where the containers are running or open another terminal window and go to the folder where the &lt;strong&gt;docker-compose.yml&lt;/strong&gt; file resided and inveok the &lt;code&gt;docker-compose stop&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Let's try to play around with the &lt;code&gt;docker-compose&lt;/code&gt; command and overwrite the folder's name in the container name to &lt;strong&gt;awesome-hooks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Run the following command to get more info on the options: &lt;code&gt;docker-compose --help&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
Options:
  -f, --file FILE             Specify an alternate compose file
                              (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name
                              (default: directory name)
  --profile NAME              Specify a profile to enable
  -c, --context NAME          Specify a context name
  --verbose                   Show more output
  --log-level LEVEL           Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  --ansi (never|always|auto)  Control when to print ANSI control characters
  --no-ansi                   Do not print ANSI control characters (DEPRECATED)
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the
                              name specified in the client certificate
  --project-directory PATH    Specify an alternate working directory
                              (default: the path of the Compose file)
  --compatibility             If set, Compose will attempt to convert keys
                              in v3 files to their non-Swarm equivalent (DEPRECATED)
  --env-file PATH             Specify an alternate environment file
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This option key is the important for us:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

  -p, --project-name NAME     Specify an alternate project name
                              (default: directory name)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Knowing that we are able to change the name for the whole docker-compose project: &lt;code&gt;docker-compose --project-name awesome-hooks up&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Creating awesome-hooks_postgres-db_1 ... done
Attaching to awesome-hooks_postgres-db_1
postgres-db_1  | The files belonging to this database system will be owned by user "postgres".
postgres-db_1  | This user must also own the server process.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now let's check the running containers with &lt;code&gt;docker ps&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS        PORTS                                     NAMES
d9601ab32d6d   postgres:14 "docker-entrypoint.s…"   11 seconds ago   Up 10 seconds 0.0.0.0:5432-&amp;gt;5432/tcp, :::5432-&amp;gt;5432/tcp awesome-hooks_postgres-db_1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The name of the container now is &lt;strong&gt;awesome-hooks_postgres-db_1&lt;/strong&gt;, so the &lt;strong&gt;awesome-hooks&lt;/strong&gt; is now used as the prefix. &lt;strong&gt;AWESOME&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Just to make sure to write it down as well, all Docker objects (containers, networks, volumes etc.) are having that specific prefixes, not only the containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combine Docker-Compose with Git Hooks
&lt;/h2&gt;

&lt;p&gt;Think about it, you are in a middle of a big feature, you have already made changes to the application's database schema, you added new tables, new columns (that are not nullable), and you have written tons of code and then BOOM.&lt;/p&gt;

&lt;p&gt;A new message arrives: &lt;strong&gt;Hey, can you check this pull request and write some feedback? - Yes of course! Let me see it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tons of changes in the source code and in the database schema, but you are a good developer, you always check out the code, and you always run the newly created tests, IT tests whatever...&lt;/p&gt;

&lt;p&gt;You also know that you have created test data for your new development by hand, and you do not want to lose it, but if you check out and recreate your database you can lose all your "temporary" test data.&lt;/p&gt;

&lt;p&gt;There are a tons of good approaches to this kind of situation, like creating a snapshot of your database, or renaming the container and the attached volumes, but it takes time, and you are in a "hurry".&lt;/p&gt;

&lt;p&gt;How about creating new containers per git branches? When a branch is being checked out, a new docker-compose project would be launched, the other containers would be stopped, and new containers would be created where the docker-compose project's name would include the git branch name.&lt;/p&gt;

&lt;p&gt;For example, a docker-compose file with the previous Postgres service on a branch named &lt;strong&gt;feature/ABC-1123-User-profile-management&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Name our project &lt;strong&gt;Warp&lt;/strong&gt; and in this case the container name could be: &lt;strong&gt;warp-feature/ABC-1123-User-profile-management_postgres-db_1&lt;/strong&gt; and if the earlier containers are stopped, there will be no collisions with the ports, all your previous data would be retained in the other container and your "work" will not be lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Let's create a folder and in that a new git repository with the &lt;code&gt;git init&lt;/code&gt; command.&lt;br&gt;
Create a docker-compose file with the following content as before, &lt;strong&gt;make sure to stop the previously created containers&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:14&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-password&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add this file to the staging area and commit it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

git add docker-compose.yml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[master (root-commit) 4a99ad5] Initial commit


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We would like to create a script, that will be copied to the git hook's directory, and it will be launched at every branch checkout.&lt;/p&gt;

&lt;p&gt;The script must be made with the name &lt;code&gt;post-checkout&lt;/code&gt; and it must be copied to the &lt;code&gt;.git/hooks&lt;/code&gt; folder, and execute permission must be given to it, so invoke &lt;code&gt;chmod +x .git/hooks/post-checkout&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;From the official documentation we can see that 3 parameters are going to given to that upon execution: &lt;a href="https://git-scm.com/docs/githooks#_post_checkout" rel="noopener noreferrer"&gt;https://git-scm.com/docs/githooks#_post_checkout&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$1 - Previous HEAD&lt;/li&gt;
&lt;li&gt;$2 - New HEAD&lt;/li&gt;
&lt;li&gt;$3 - 1 if checking out a branch, 0 if checking out something else, such as a file (rollbacks)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we are going to receive the SHA values of the HEAD commit, rather than the branch names we are looking forward.&lt;/p&gt;

&lt;p&gt;Let's add some code into the shell script:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Previous head:"&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Current head:"&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Branch checkout:"&lt;/span&gt; &lt;span class="nv"&gt;$3&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After that create a new branch with the checkout or switch command: &lt;code&gt;git checkout -b test-branch&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Switched to a new branch 'test-branch'
Previous head: 4a99ad545a28e22b5774ae2fa90040fdc86e4b7a
Current head: 4a99ad545a28e22b5774ae2fa90040fdc86e4b7a
Branch checkout: 1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can immediately see one of the problem, if we would use those variables for the project name it would be super confusing, these SHA values are meaning nothing to the developer, and they can be the same if you start a new branch, so we have to come up with a better solution.&lt;/p&gt;

&lt;p&gt;With the following command you can get the name of the current branch: &lt;code&gt;git branch --show-current&lt;/code&gt; (Available in &lt;a href="https://git-scm.com/docs/git-branch/2.22.0#Documentation/git-branch.txt---show-current" rel="noopener noreferrer"&gt;Git 2.22.0&lt;/a&gt;) - For earlier versions it is tricky, but this must be working: &lt;code&gt;git rev-parse --abbrev-ref HEAD&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

test-branch


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Cool we are able to determine the branch we are on, but how can we identify the branch we are coming from?&lt;/p&gt;

&lt;p&gt;So in theory we would like to &lt;strong&gt;STOP&lt;/strong&gt; the containers from the branch we are switching from and &lt;strong&gt;START&lt;/strong&gt; new containers.&lt;/p&gt;

&lt;p&gt;I would like to propose two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use the &lt;code&gt;git name-rev --name-only &amp;lt;sha&amp;gt;&lt;/code&gt; command to retrieve it from the $1 argument&lt;/li&gt;
&lt;li&gt;We save the current branch name (in this case the previous branch) to a file named &lt;strong&gt;.git/earlier-branch&lt;/strong&gt;, and then we would use it in the process&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;".git/earlier-branch"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"File: .git/earlier-branch does not exist, creating with current branch"&lt;/span&gt;
    &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;EARLIER_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/earlier-branch
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Loading: .git/earlier-branch"&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .git/earlier-branch
&lt;span class="nv"&gt;UPCOMING_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Before switch branch: &lt;/span&gt;&lt;span class="nv"&gt;$EARLIER_BRANCH&lt;/span&gt;&lt;span class="s2"&gt; - Upcoming branch: &lt;/span&gt;&lt;span class="nv"&gt;$UPCOMING_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;EARLIER_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/earlier-branch


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This snippet on the first launch will create the file that will have a variable named &lt;strong&gt;EARLIER_BRANCH&lt;/strong&gt; having value of the &lt;code&gt;$(git branch --show-current)&lt;/code&gt; result. It may fail on the first try, but after it will be relatively consistent.&lt;/p&gt;

&lt;p&gt;Okay, the 3rd parameter describes if this operation is a branch change or just a file checkout, so if it's value is &lt;strong&gt;1 (one)&lt;/strong&gt; it means it is a branch "change", we have to check it.&lt;/p&gt;

&lt;p&gt;Let's add the &lt;code&gt;docker-compose&lt;/code&gt; commands into the script.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Post checkout starting"&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Args passed to this are:&lt;/span&gt;
&lt;span class="c"&gt;# $1 - Previous HEAD&lt;/span&gt;
&lt;span class="c"&gt;# $2 - New HEAD&lt;/span&gt;
&lt;span class="c"&gt;# $3 - 1 if checking out a branch, 0 if checking out something else, such as a file (rollbacks)&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$3&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;".git/earlier-branch"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"File: .git/earlier-branch does not exist, creating with current branch"&lt;/span&gt;
        &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;EARLIER_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/earlier-branch
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Loading: .git/earlier-branch"&lt;/span&gt;
    &lt;span class="nb"&gt;source&lt;/span&gt; .git/earlier-branch
    &lt;span class="nv"&gt;UPCOMING_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Before switch branch: &lt;/span&gt;&lt;span class="nv"&gt;$EARLIER_BRANCH&lt;/span&gt;&lt;span class="s2"&gt; - Upcoming branch: &lt;/span&gt;&lt;span class="nv"&gt;$UPCOMING_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    docker-compose &lt;span class="nt"&gt;--project-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;warp-&lt;span class="nv"&gt;$EARLIER_BRANCH&lt;/span&gt; stop
    docker-compose &lt;span class="nt"&gt;--project-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;warp-&lt;span class="nv"&gt;$UPCOMING_BRANCH&lt;/span&gt; up &lt;span class="nt"&gt;-d&lt;/span&gt;

    &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;EARLIER_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/earlier-branch
&lt;span class="k"&gt;fi&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We are on the &lt;strong&gt;test-branch&lt;/strong&gt; now, let's go back to the master: &lt;code&gt;git checkout master&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Switched to branch 'master'
Post checkout starting
File: .git/earlier-branch does not exist, creating with current branch
Loading: .git/earlier-branch
Before switch branch: master - Upcoming branch: master
Creating warp-master_postgres-db_1 ... done


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can see the interference in the power: "Before switch branch: master - Upcoming branch: master" - This is not true, and this is the problem with this branch resolving strategy, the very first time it will not work, because the &lt;strong&gt;.git/earlier-branch&lt;/strong&gt; file does not exist, but it can be created by hand on the clone, or with some other script.&lt;/p&gt;

&lt;p&gt;Now if we would run the &lt;code&gt;docker ps&lt;/code&gt; we should see the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS                 PORTS                                     NAMES
674ca666fe1c   postgres:14  "docker-entrypoint.s…"   2 minutes ago   Up About a minute      0.0.0.0:5432-&amp;gt;5432/tcp, :::5432-&amp;gt;5432/tcp warp-master_postgres-db_1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The name of the container is &lt;strong&gt;warp-master_postgres-db_1&lt;/strong&gt;, which we wanted! Yes!!!&lt;/p&gt;

&lt;p&gt;Now let's switch back to the &lt;strong&gt;test-branch&lt;/strong&gt; or create a brand new one, like we mentioned earlier above: &lt;code&gt;git checkout -b feature/ABC-1123-User-profile-management&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Switched to a new branch 'feature/ABC-1123-User-profile-management'
Post checkout starting
Loading: .git/earlier-branch
Before switch branch: master - Upcoming branch: feature/ABC-1123-User-profile-management
Stopping warp-master_postgres-db_1 ... done
Creating network "warp-featureabc-1123-user-profile-management_default" with the default driver
Creating warp-featureabc-1123-user-profile-management_postgres-db_1 ... done


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can see that the &lt;strong&gt;warp-master_postgres-db_1&lt;/strong&gt; container is being stopped, and after that the &lt;code&gt;warp-featureabc-1123-user-profile-management_postgres-db_1&lt;/code&gt; container is being created. Awesome! This is what we wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve the experience
&lt;/h2&gt;

&lt;p&gt;Me personally if I check out a project and I see a &lt;strong&gt;docker-compose.yml&lt;/strong&gt; file I check it and start it from the IntelliJ IDEA after I imported the project. But there are other people who prefer to start it from the terminal with the &lt;code&gt;docker-compose&lt;/code&gt; executable, which is totally understandable. With these methods the created git hook will not work properly so I propose to create a few helper scripts which can be used from the terminal and the hook also can depend on it.&lt;/p&gt;

&lt;p&gt;Let's create a &lt;strong&gt;start-infrastructure.sh&lt;/strong&gt; that will contain the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;GIT_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"warp-&lt;/span&gt;&lt;span class="nv"&gt;$GIT_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting docker compose project with name: &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
docker-compose &lt;span class="nt"&gt;--project-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt; up &lt;span class="nt"&gt;-d&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And let's create a &lt;strong&gt;stop-infrastructure.sh&lt;/strong&gt; that will contain the following lines:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;GIT_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"warp-&lt;/span&gt;&lt;span class="nv"&gt;$GIT_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping docker compose project with name: &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
docker-compose &lt;span class="nt"&gt;--project-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt; stop


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These scripts can be used to start and stop the containers, and they can be parametrized, but if no parameters are given to it, the current git branch will be used.&lt;/p&gt;

&lt;p&gt;Let's rework the git hook:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Post checkout starting"&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Args passed to this are:&lt;/span&gt;
&lt;span class="c"&gt;# $1 - Previous HEAD&lt;/span&gt;
&lt;span class="c"&gt;# $2 - New HEAD&lt;/span&gt;
&lt;span class="c"&gt;# $3 - 1 if checking out a branch, 0 if checking out something else, such as a file (rollbacks)&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$3&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;".git/earlier-branch"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"File: .git/earlier-branch does not exist, creating with current branch"&lt;/span&gt;
        &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;EARLIER_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/earlier-branch
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Loading: .git/earlier-branch"&lt;/span&gt;
    &lt;span class="nb"&gt;source&lt;/span&gt; .git/earlier-branch
    &lt;span class="nv"&gt;UPCOMING_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Before switch branch: &lt;/span&gt;&lt;span class="nv"&gt;$EARLIER_BRANCH&lt;/span&gt;&lt;span class="s2"&gt; - Upcoming branch: &lt;/span&gt;&lt;span class="nv"&gt;$UPCOMING_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    ./stop-infrastructure.sh &lt;span class="nv"&gt;$EARLIER_BRANCH&lt;/span&gt; &lt;span class="c"&gt;# Changed lines&lt;/span&gt;
    ./start-infrastructure.sh &lt;span class="nv"&gt;$UPCOMING_BRANCH&lt;/span&gt;  &lt;span class="c"&gt;# Changed lines&lt;/span&gt;

    &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;EARLIER_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git/earlier-branch
&lt;span class="k"&gt;fi&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Check all containers
&lt;/h2&gt;

&lt;p&gt;If we run &lt;code&gt;docker ps -a&lt;/code&gt; we are going to see all the containers we created, so we can easily verify that the containers are still there and if we want to just restart any we can do it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

CONTAINER ID   IMAGE        COMMAND                  CREATED        STATUS                      PORTS    NAMES
188995a19b3f   postgres:14  "docker-entrypoint.s…"   5 days ago     Exited (0) 10 minutes ago            docker-env-with-git-hooks_postgres-db_1
5d01690861ac   postgres:14  "docker-entrypoint.s…"   7 days ago     Exited (0) 10 minutes ago            warp-featureabc-1123-user-profile-management_postgres-db_1
674ca666fe1c   postgres:14  "docker-entrypoint.s…"   7 days ago     Exited (0) 10 minutes ago            warp-master_postgres-db_1
e8b534a4bcd9   postgres:14  "docker-entrypoint.s…"   7 days ago     Exited (0) 10 minutes ago            awesome-hooks_postgres-db_1



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Combine it with Maven
&lt;/h2&gt;

&lt;p&gt;To make sure every developer on the project is having the same boost for their workflows we can use a Maven plugin to copy this script to their &lt;strong&gt;.git/hooks&lt;/strong&gt; folder.&lt;/p&gt;

&lt;p&gt;Create a folder named &lt;strong&gt;hooks&lt;/strong&gt; and put the &lt;strong&gt;post-checkout&lt;/strong&gt; file into, after that configure the following Maven plugin in your project:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.rudikershaw.gitbuildhook&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;git-build-hook-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.3.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;installHooks&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;post-checkout&amp;gt;&lt;/span&gt;hooks/post-checkout&lt;span class="nt"&gt;&amp;lt;/post-checkout&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/installHooks&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;install-hooks&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;initialize&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;install&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To make sure that the created scripts are going to be executable for other developers as well, we have to add the execution permission to all via a Git command (&lt;strong&gt;but first add them to the staging area&lt;/strong&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git add hooks/post-checkout start-infrastructure.sh stop-infrastructure.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git update-index --chmod=+x hooks/post-checkout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git update-index --chmod=+x start-infrastructure.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git update-index --chmod=+x stop-infrastructure.sh&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Maybe this was a "long" journey, but I hope you liked it, I'm still experimenting with this setup, because you may not want to create a new environment for all of your new branches, but if that is the case, I think it is a good start.&lt;/p&gt;

&lt;p&gt;One more addition could be introduced, when a local branch is being deleted maybe a cleanup script could be executed to delete the "dangling" docker-compose projects.&lt;/p&gt;

&lt;p&gt;I'm curious about your opinion on the topic, have you tried to do the same? Do you know a better/more professional approach to the problem? If yes please let me know down in the comments.&lt;/p&gt;

&lt;p&gt;If you want to follow me you can do it on the following places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nandorholozsnyak/docker-env-with-git-hooks" rel="noopener noreferrer"&gt;GitHub Repository with the examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nandorholozsnyak" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/TheRealHNK" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/nandor-holozsnyak/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Creator of the cover image: &lt;a href="https://unsplash.com/@carrier_lost" rel="noopener noreferrer"&gt;https://unsplash.com/@carrier_lost&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>docker</category>
      <category>focus</category>
      <category>productivity</category>
    </item>
    <item>
      <title>JBang + Quarkus + AWS Lambda + Terraform = &lt;3</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Sun, 02 Jan 2022 20:55:15 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/jbang-quarkus-aws-lambda-terraform-3-4ahk</link>
      <guid>https://dev.to/nandorholozsnyak/jbang-quarkus-aws-lambda-terraform-3-4ahk</guid>
      <description>&lt;p&gt;Have you ever wondered how can you utilize your Java skills in the serverless world?&lt;br&gt;
If not then let me take you on a small trip where we will create AWS Lambda functions with Java, and yes with &lt;a href="https://jbang.dev"&gt;JBang&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jbang.dev/documentation/guide/latest/installation.html"&gt;JBang&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Properly configured &lt;a href="https://aws.amazon.com/cli/"&gt;AWS CLI&lt;/a&gt; with Access and Secret key&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; - latest/newest release would work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would not like to go through how these tools should be installed, I assume these things are a piece of cake.&lt;/p&gt;
&lt;h5&gt;
  
  
  My versions
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ jbang version
0.86.0
❯ aws --version
aws-cli/1.22.5 Python/3.9.5 Linux/5.15.8-76051508-generic botocore/1.23.5
❯ terraform -v
Terraform v1.0.0
on linux_amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  JBang
&lt;/h3&gt;

&lt;p&gt;JBang is a powerful tool which lets you create &lt;code&gt;.java&lt;/code&gt; files with your requested dependencies, and with different commands you will be able to build and export &lt;code&gt;.jar&lt;/code&gt; files and native binaries.&lt;/p&gt;

&lt;p&gt;Okay okay, we have Maven and Gradle, why would I need this?&lt;/p&gt;

&lt;p&gt;For my answer for this is the following: If you really want to code just a small app with some dependencies rather than creating and maintaining a project with a &lt;code&gt;pom.xml&lt;/code&gt; or a &lt;code&gt;gradle.build&lt;/code&gt; could be overkill, like in the following use case where we are going to create a Lambda function.&lt;/p&gt;
&lt;h3&gt;
  
  
  My motivation
&lt;/h3&gt;

&lt;p&gt;I have attended projects where all the Lambda functions were written in Python, and I'm not a Python developer, of course another programming language, can be learned easily, but with deadlines on our back, if the team is more of a Java team, then writing Lambda functions in Java makes more sense.&lt;br&gt;&lt;br&gt;
What I really like in the Python or JavaScript based Lambda functions are their "lightness", the authors of the functions created a small &lt;code&gt;.py&lt;/code&gt; or &lt;code&gt;.js&lt;/code&gt; file, and they could deploy it and invoke it and of course they have the online code editor, with Java we won't have that feature, but we can utilize our Java knowledge. Of course dependency management should happen if we need external dependencies, with Python I know it is relatively easy, and of course with Java too, Maven and Gradle are beautiful tools, but I think they are overkill for smaller functions.&lt;/p&gt;

&lt;p&gt;I really wanted to have almost the same "workflow" with Java, just one &lt;code&gt;.java&lt;/code&gt; file per function that can have external dependencies (like &lt;a href="https://quarkus.io"&gt;Quarkus&lt;/a&gt; that we are also going to use because it has a really nice integration with AWS Lambda as well) listed somewhere in the &lt;code&gt;.java&lt;/code&gt; source file as well and can be built by anybody who has the &lt;code&gt;jbang&lt;/code&gt; binary on their workstation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Our first JBang "script"
&lt;/h3&gt;

&lt;p&gt;After JBang got installed we can start working with that, let's create our very first script with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ jbang init hellojbang.java
[jbang] File initialized. You can now run it with 'jbang hellojbang.java' or edit it using 'jbang edit --open=[editor] hellojbang.java' where [editor] is your editor or IDE, e.g. 'idea'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the file is created we have a "few" options to edit it. We can use the command it outputs with our installed IDE (IDEA,VSCode): &lt;code&gt;jbang edit --open=idea hellojbang.java&lt;/code&gt;.&lt;br&gt;
At first glance it could be a bit "weird", I was talking about having no build tool involved in the flow, but we see a &lt;code&gt;build.gradle&lt;/code&gt; file, but do not worry, this is just a small helper project that was created for it, to have IDE support, as you can see the whole project sits in the &lt;code&gt;~/.jbang/cache&lt;/code&gt; folder and a symbolic link was created for it. &lt;/p&gt;

&lt;p&gt;For IntelliJ IDEA JBang has a really nice plugin, really young, few weeks old but can do the work: &lt;a href="https://plugins.jetbrains.com/plugin/18257-jbang"&gt;https://plugins.jetbrains.com/plugin/18257-jbang&lt;/a&gt; in this case you do not have to use the &lt;code&gt;edit&lt;/code&gt; command, because IDEA will have a feature to download sync all dependencies and have code completion.&lt;/p&gt;

&lt;p&gt;If we open the file we will see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;


&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;hellojbang&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run the &lt;code&gt;.java&lt;/code&gt; file with the following commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jbang hellojbang.java&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jbang run hellojbang.java&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;./hellojbang.java&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output will be the following every time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ ./hellojbang.java 
[jbang] Building jar...
Hello World
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first run JBang creates a &lt;code&gt;.jar&lt;/code&gt; file within its cache folder and it runs it, if codes has no changes compared to earlier run then it will not build it again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring dependencies and Java version
&lt;/h3&gt;

&lt;p&gt;JBang uses &lt;code&gt;//&lt;/code&gt; based directives to configure the dependencies for the application, and other things as well.&lt;br&gt;
Let's see how we can add some dependencies and set the Java version to 11, because with the AWS Lambda we will only have a Java 11 runtime environment.&lt;/p&gt;

&lt;p&gt;We can add dependencies with the &lt;code&gt;//DEPS &amp;lt;gav&amp;gt;&lt;/code&gt; directive and we can set the Java version to 11 with &lt;code&gt;//JAVA 11&lt;/code&gt; directive&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA 11&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS org.apache.commons:commons-lang3:3.12.0&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.commons.lang3.StringUtils&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;hellojbang&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;abbreviate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building and running the script and the output will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ jbang hellojbang.java
[jbang] Building jar...
H...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice, we added a dependency and set the Java version to 11. We can add unlimited amount of dependencies and we can use BOMs as well.&lt;br&gt;
That was a brief introduction to JBang and now let's see the AWS stuff.&lt;/p&gt;
&lt;h3&gt;
  
  
  Quarkus &amp;amp; JBang &amp;amp; AWS Lambda &amp;amp; Terraform
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Quarkus &amp;amp; JBang
&lt;/h4&gt;

&lt;p&gt;Create a new &lt;code&gt;.java&lt;/code&gt; file where we can write out Lambda function code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ jbang init AwsLambdaFunction.java
[jbang] File initialized. You can now run it with 'jbang AwsLambdaFunction.java' or edit it using 'jbang edit --open=[editor] AwsLambdaFunction.java' where [editor] is your editor or IDE, e.g. 'idea'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the file within our favourite editor: &lt;code&gt;jbang edit --open=idea AwsLambdaFunction.java&lt;/code&gt; and add the following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-bom:2.6.0.Final@pom&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-amazon-lambda&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that we state that we would like to use Quarkus at the "newest" version: 2.6.0 and we are adding a new dependency to the "project" as well: &lt;code&gt;io.quarkus:quarkus-amazon-lambda&lt;/code&gt;. We don't have to provide the version number, JBang is smart enough to have this information from the BOM specified above it.&lt;/p&gt;

&lt;p&gt;If we want to create a Lambda function with Quarkus we have to implement the &lt;code&gt;com.amazonaws.services.lambda.runtime.RequestHandler&lt;/code&gt; interface by implementing the &lt;code&gt;handleRequest&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA 11&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-bom:2.6.0.Final@pom&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-amazon-lambda&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS org.projectlombok:lombok:1.18.22&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.amazonaws.services.lambda.runtime.Context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.amazonaws.services.lambda.runtime.RequestHandler&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jboss.logging.Logger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AwsLambdaFunction&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LambdaInput&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LambdaOutput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="no"&gt;LOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AwsLambdaFunction"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AwsLambdaFunction&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LambdaOutput&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LambdaInput&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Lambda: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;LambdaOutput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Incoming text: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInput&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LambdaInput&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LambdaOutput&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the "final" code snippet we can some new things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;//DEPS org.projectlombok:lombok:1.18.22&lt;/code&gt; - Lombok, which is here to make the POJO classes thinner in the code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager&lt;/code&gt; - We would like to log, in this case we need a logger configuration.&lt;/li&gt;
&lt;li&gt;We must have a public no-args constructor.&lt;/li&gt;
&lt;li&gt;POJOs should be conventional Beans, with no-arg constructors and with getter/setter pairs.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ jbang AwsLambdaFunction.java
[jbang] Resolving dependencies...
[jbang] Artifacts used for dependency management:
         io.quarkus:quarkus-bom:pom:2.6.0.Final
[jbang] io.quarkus:quarkus-amazon-lambda
         org.projectlombok:lombok:jar:1.18.22
Done
[jbang] Dependencies resolved
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Jan 02, 2022 9:14:53 PM org.jboss.threads.Version &amp;lt;clinit&amp;gt;
INFO: JBoss Threads version 3.4.2.Final
Jan 02, 2022 9:14:53 PM io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 610ms
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,&amp;lt; / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2022-01-02 21:14:54,510 INFO  [io.quarkus] (main) Quarkus 2.6.0.Final on JVM started in 0.428s. 
2022-01-02 21:14:54,515 INFO  [io.quarkus] (main) Profile prod activated. 
2022-01-02 21:14:54,516 INFO  [io.quarkus] (main) Installed features: [amazon-lambda, cdi]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It means basically our code is using Quarkus and we are "almost done". Of course it would be nice to test it, right now we are not going to write unit tests for it, we would be able to, lets cover that topic in another time, right now just utilize Quarkus's dev mode with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ jbang -Dquarkus.dev AwsLambdaFunction.java
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
2022-01-02 21:17:08,318 INFO  [io.qua.ama.lam.run.MockEventServer] (build-10) Mock Lambda Event Server Started
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,&amp;lt; / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2022-01-02 21:17:08,647 INFO  [io.qua.ama.lam.run.AbstractLambdaPollLoop] (Lambda Thread (DEVELOPMENT)) Listening on: http://localhost:8080/_lambda_/2018-06-01/runtime/invocation/next

2022-01-02 21:17:08,654 INFO  [io.quarkus] (Quarkus Main Thread) Quarkus 2.6.0.Final on JVM started in 1.135s. 
2022-01-02 21:17:08,659 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2022-01-02 21:17:08,659 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [amazon-lambda, cdi]

--
Tests paused
Press [r] to resume testing, [o] Toggle test output, [h] for more options&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the dev mode, Quarkus will start a mock HTTP event server so we can use &lt;code&gt;curl&lt;/code&gt; or other tools to invoke an HTTP endpoint where we can POST our input object, and then we can examine the result/response as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
          &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
        }"&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"result"&lt;/span&gt;:&lt;span class="s2"&gt;"Incoming text: Hello World"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;%  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, using Quarkus's dev mode lets you change the code "on-the-fly", and on the next invocation it will rebuild automatically. You do not have to build it every time by yourself.&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS Lambda &amp;amp; Terraform
&lt;/h4&gt;

&lt;p&gt;Make sure the AWS CLI is configured: &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's deploy our code to AWS with Terraform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Terraform basic configuration&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.70.0"&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.1.0"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Set AWS region to eu-central-1 -&amp;gt; Frankfurt&lt;/span&gt;
&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# We have to create a role for the Lambda function, it is mandatory.&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"iam_for_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"iam_for_lambda_function"&lt;/span&gt;

  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# We have to somehow create the jar file that we will deploy, we are going to use the "local-exec" provision feature for it.&lt;/span&gt;
&lt;span class="c1"&gt;# First we will build the .java file, then we have to export it, export means we are copying it from the jbang cache to the current working directory&lt;/span&gt;
&lt;span class="c1"&gt;# After that we have to update the jar file, we have to move the exported "lib" folder to the jar file, we have to bundle all dependencies that we are relaying on.&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"jar_file"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AwsLambdaFunction.jar"&lt;/span&gt;
  &lt;span class="nx"&gt;content_base64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filebase64sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"AwsLambdaFunction.java"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"jbang build --fresh AwsLambdaFunction.java &amp;amp;&amp;amp; jbang export portable --fresh --force AwsLambdaFunction.java &amp;amp;&amp;amp; jar uf AwsLambdaFunction.jar lib/"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Lambda function we want to create and invoke.&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"function"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jar_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;
  &lt;span class="nx"&gt;source_code_hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jar_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content_base64&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AwsLambdaFunction"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iam_for_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="c1"&gt;#Handler method must be io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jar_file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"java11"&lt;/span&gt;
  &lt;span class="nx"&gt;memory_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run the following commands:&lt;/p&gt;

&lt;p&gt;First we have to call the &lt;code&gt;terraform init&lt;/code&gt;, it will initialize the terraform state, and after that we can call &lt;code&gt;terraform plan&lt;/code&gt; or &lt;code&gt;terraform apply&lt;/code&gt;. &lt;code&gt;plan&lt;/code&gt; will just only show what it would do if &lt;code&gt;apply&lt;/code&gt; would be called.&lt;br&gt;
After calling &lt;code&gt;terraform apply&lt;/code&gt; we have to write &lt;code&gt;yes&lt;/code&gt; when it asks for approval.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "~&amp;gt; 3.70.0"...
- Finding hashicorp/local versions matching "2.1.0"...
- Installing hashicorp/aws v3.70.0...
- Installed hashicorp/aws v3.70.0 (signed by HashiCorp)
- Installing hashicorp/local v2.1.0...
- Installed hashicorp/local v2.1.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

...
❯ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_iam_role.iam_for_lambda will be created
  + resource "aws_iam_role" "iam_for_lambda" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "lambda.amazonaws.com"
                        }
                      + Sid       = ""
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "iam_for_lambda"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = (known after apply)
      + unique_id             = (known after apply)

      + inline_policy {
          + name   = (known after apply)
          + policy = (known after apply)
        }
    }

  # aws_lambda_function.function will be created
  + resource "aws_lambda_function" "function" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + filename                       = "AwsLambdaFunction.jar"
      + function_name                  = "AwsLambdaFunction"
      + handler                        = "io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "java11"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + source_code_hash               = "zxCVmQSXmb7Zf3EPLyKKVgL5Tv61WGLArpHz8QSum2c="
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 3
      + version                        = (known after apply)

      + tracing_config {
          + mode = (known after apply)
        }
    }

  # local_file.jar_file will be created
  + resource "local_file" "jar_file" {
      + content_base64       = "zxCVmQSXmb7Zf3EPLyKKVgL5Tv61WGLArpHz8QSum2c="
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "AwsLambdaFunction.jar"
      + id                   = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After entring &lt;code&gt;yes&lt;/code&gt; and pressing Enter we should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local_file.jar_file: Creating...
local_file.jar_file: Provisioning with 'local-exec'...
local_file.jar_file (local-exec): Executing: ["/bin/sh" "-c" "jbang build --fresh AwsLambdaFunction.java &amp;amp;&amp;amp; jbang export portable --fresh --force AwsLambdaFunction.java &amp;amp;&amp;amp; jar uf AwsLambdaFunction.jar lib/"]
local_file.jar_file (local-exec): [jbang] Resolving dependencies...
local_file.jar_file (local-exec): [jbang] Artifacts used for dependency management:
local_file.jar_file (local-exec):          io.quarkus:quarkus-bom:pom:2.6.0.Final
local_file.jar_file (local-exec): [jbang] io.quarkus:quarkus-amazon-lambda
local_file.jar_file (local-exec):          org.projectlombok:lombok:jar:1.18.22
local_file.jar_file (local-exec): Done
local_file.jar_file (local-exec): [jbang] Dependencies resolved
local_file.jar_file (local-exec): [jbang] Building jar...
aws_iam_role.iam_for_lambda: Creating...
local_file.jar_file (local-exec): [jbang] Post build with io.quarkus.launcher.JBangIntegration
local_file.jar_file (local-exec): Jan 02, 2022 9:40:29 PM org.jboss.threads.Version &amp;lt;clinit&amp;gt;
local_file.jar_file (local-exec): INFO: JBoss Threads version 3.4.2.Final
local_file.jar_file (local-exec): Jan 02, 2022 9:40:30 PM io.quarkus.deployment.QuarkusAugmentor run
local_file.jar_file (local-exec): INFO: Quarkus augmentation completed in 652ms
aws_iam_role.iam_for_lambda: Creation complete after 2s [id=iam_for_lambda_function]
local_file.jar_file (local-exec): [jbang] Resolving dependencies...
local_file.jar_file (local-exec): [jbang] Artifacts used for dependency management:
local_file.jar_file (local-exec):          io.quarkus:quarkus-bom:pom:2.6.0.Final
local_file.jar_file (local-exec): [jbang] io.quarkus:quarkus-amazon-lambda
local_file.jar_file (local-exec):          org.projectlombok:lombok:jar:1.18.22
local_file.jar_file (local-exec): Done
local_file.jar_file (local-exec): [jbang] Dependencies resolved
local_file.jar_file (local-exec): [jbang] Building jar...
local_file.jar_file (local-exec): [jbang] Post build with io.quarkus.launcher.JBangIntegration
local_file.jar_file (local-exec): Jan 02, 2022 9:40:33 PM org.jboss.threads.Version &amp;lt;clinit&amp;gt;
local_file.jar_file (local-exec): INFO: JBoss Threads version 3.4.2.Final
local_file.jar_file (local-exec): Jan 02, 2022 9:40:34 PM io.quarkus.deployment.QuarkusAugmentor run
local_file.jar_file (local-exec): INFO: Quarkus augmentation completed in 705ms
local_file.jar_file (local-exec): [jbang] Updating jar manifest
local_file.jar_file (local-exec): [jbang] Exported to /media/nandi/Data/VCS/GIT/jbang-terraform-aws/devto/AwsLambdaFunction.jar
local_file.jar_file: Creation complete after 8s [id=352a94713061363fa798146c96e188a5dd35a975]
aws_lambda_function.function: Creating...
aws_lambda_function.function: Creation complete after 8s [id=AwsLambdaFunction]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to test it from the AWS console you can do it &lt;a href="https://eu-central-1.console.aws.amazon.com/lambda/home?region=eu-central-1#/functions/AwsLambdaFunction?tab=testing"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to use the AWS CLI invoke the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hello World from AWS Lambda&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; payload.json
aws lambda invoke response.txt &lt;span class="nt"&gt;--function-name&lt;/span&gt; AwsLambdaFunction &lt;span class="nt"&gt;--log-type&lt;/span&gt; Tail &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'LogResult'&lt;/span&gt; &lt;span class="nt"&gt;--payload&lt;/span&gt; file://payload.json | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should look like this (will be different for you, date-time and IDs):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;START RequestId: b58e9171-4d9c-4d92-8056-aa7e20317619 Version: $LATEST
2022-01-02 20:45:51,864 INFO  [RequestHandlerExample] (main) Hello from Lambda: LambdaInput(input=Hello World from AWS Lambda)
END RequestId: b58e9171-4d9c-4d92-8056-aa7e20317619
REPORT RequestId: b58e9171-4d9c-4d92-8056-aa7e20317619  Duration: 1.47 ms       Billed Duration: 2 ms   Memory Size: 256 MB     Max Memory Used: 118 MB 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we make any changes to our &lt;code&gt;.java&lt;/code&gt; file and we want to deploy it to AWS, we just have to run terraform to do the heavy lifting for us.&lt;/p&gt;

&lt;h4&gt;
  
  
  Outro
&lt;/h4&gt;

&lt;p&gt;It is a quick and brief article on how to create and deploy Java based functions to AWS Lambda using JBang and Terraform. I really like all the used technologies here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One thing before I close the article&lt;/strong&gt;: Quarkus is NOT a mandatory framework to use, I just used it because I really love working with that, and if the function would need database handling libs or would like to use dependency injection then we would be able to just add more and more dependencies to it and use it. We just barely touched the topic.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://twitter.com/TheRealHNK"&gt;Twitter@TheRealHNK&lt;/a&gt; for more good stuff,&lt;/p&gt;

&lt;p&gt;If you would like to learn more please check out the following sites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jbang.dev/documentation/guide/latest/intro.html"&gt;https://www.jbang.dev/documentation/guide/latest/intro.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://quarkus.io/guides/amazon-lambda"&gt;https://quarkus.io/guides/amazon-lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://quarkus.io/guides/funqy-amazon-lambda"&gt;https://quarkus.io/guides/funqy-amazon-lambda&lt;/a&gt; - Funq is a Quarkus based super thing. Take a look on it.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function"&gt;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Upcoming
&lt;/h4&gt;

&lt;p&gt;I'm planning to make new articles about exploring AWS Lambda triggers like SQS, S3, SNS. Stay tuned!&lt;/p&gt;




&lt;p&gt;Cover (Photo by Gábor Molnár): &lt;a href="https://unsplash.com/photos/Y7ufx8R8PM0"&gt;https://unsplash.com/photos/Y7ufx8R8PM0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>jbang</category>
      <category>aws</category>
      <category>terraform</category>
    </item>
    <item>
      <title>JAXB Swagger Plugin - my first contribution to the Open Source world</title>
      <dc:creator>Nándor Holozsnyák</dc:creator>
      <pubDate>Sun, 23 Feb 2020 18:22:04 +0000</pubDate>
      <link>https://dev.to/nandorholozsnyak/jaxb-swagger-plugin-my-first-contribution-to-the-open-source-world-1dje</link>
      <guid>https://dev.to/nandorholozsnyak/jaxb-swagger-plugin-my-first-contribution-to-the-open-source-world-1dje</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Hello everybody and welcome to my very first post on dev.to :)&lt;br&gt;
I have been almost here for one year now (joined: Feb 24, 2019, just checked it now :D)&lt;br&gt;
I have been a developer since 2016 but I always liked computer and programming, so basically after my official job I come home and I work on side-project with friends or just for fun. My main interests are Java related topics, I like both Spring and Java EE world and I think #Hibernate is my favorite third party framework (kudos to the boys/girls at JBoss).&lt;/p&gt;

&lt;p&gt;I live and work in Debrecen, in Hungary, this is the second largest city in the country and there are a lot of opportunities for developers, the university is here for the upcoming devs, but I prefer coding related schools with more practical stuff :)&lt;/p&gt;
&lt;h1&gt;
  
  
  What did I bring to you?
&lt;/h1&gt;

&lt;p&gt;At my last job we were preferring XSD2Java class based DTO class generation with a lot of reasons and we were building microservice architecture based applications and we were providing functionalities for clients (both internal (internal applications) and external clients).&lt;/p&gt;

&lt;p&gt;The documentation for these classes and API endpoints were mandatory and we were experimenting with &lt;a href="https://swagger.io/"&gt;Swagger&lt;/a&gt;, at this time we were a using Java EE related framework called &lt;a href="https://thorntail.io/"&gt;Thorntail&lt;/a&gt; (a.k.a Wildfly Swarm), and it came really handy to just put some annotation on the classes and API endpoints to document them.&lt;/p&gt;

&lt;p&gt;The problem was the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All of our DTOs, which were part of the communication, were generated by the 
&lt;strong&gt;maven-jaxb2-plugin&lt;/strong&gt;
and we were not able to annotate our generated classes because on the next generation the whole stuff would go to the trash can (/dev/null).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solution: JAXB2 plugin supports external dependencies to take part in the class generation and we found a few options to extend our class generation phase.&lt;br&gt;
This &lt;a href="https://github.com/redlab/swagger-jaxb"&gt;Redlab Swagger JAXB Plugin&lt;/a&gt; helped us a lot but it just hepled us to annotate our fields with the proper Swagger annotations like &lt;a href="https://github.com/swagger-api/swagger-core/blob/1.5/modules/swagger-annotations/src/main/java/io/swagger/annotations/ApiModel.java"&gt;@ApiModel&lt;/a&gt; and &lt;a href="https://github.com/swagger-api/swagger-core/blob/1.5/modules/swagger-annotations/src/main/java/io/swagger/annotations/ApiModelProperty.java"&gt;@ApiModelProperty&lt;/a&gt;, nothing special, if you take a closer look at these annotations you will see that there are a lot of configuration options like, the &lt;strong&gt;required&lt;/strong&gt;, &lt;strong&gt;allowableValues&lt;/strong&gt;, &lt;strong&gt;description&lt;/strong&gt; attributes.&lt;/p&gt;

&lt;p&gt;As I quited the company, they moved to the &lt;a href="https://www.openapis.org/"&gt;OpenAPI&lt;/a&gt; specification which is a way more rich specification, I stayed with Swagger at my side-projects and at my job.&lt;br&gt;
There is no problem with Swagger, OpenAPI just got a few new ways to express schemas.&lt;/p&gt;

&lt;p&gt;I started to look around the internet, GitHub and I found that there is a lot of fork of the mentioned JAXB Plugin, and there is one which is way more expressive than the original one.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Andreas-Maier-NTT"&gt;
        Andreas-Maier-NTT
      &lt;/a&gt; / &lt;a href="https://github.com/Andreas-Maier-NTT/swagger-jaxb"&gt;
        swagger-jaxb
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JAXB XJC Plugin for automatically adding annotations from Swagger to generated classes from an XSD
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
swagger-jaxb&lt;/h1&gt;
&lt;p&gt;JAXB XJC Plugin for automatically adding annotations from Swagger to generated classes from an XSD&lt;/p&gt;
&lt;p&gt;Tests run in separate project, see here for the code &lt;a href="https://github.com/redlab/swagger-jaxb-tck"&gt;https://github.com/redlab/swagger-jaxb-tck&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
Usage&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;REQUIRE Java 8 or higher!&lt;/li&gt;
&lt;li&gt;build the plugin with maven&lt;/li&gt;
&lt;li&gt;install it in your local repo&lt;/li&gt;
&lt;li&gt;add the plugin to your classpath and use -swaggify on your jaxb command line or configure it i your pom
or&lt;/li&gt;
&lt;li&gt;add sonatype snapshot repository to your repo manager. ( post an issue if you really want dev version in Maven Central )&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;use with jaxb2-maven-plugin&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;    &amp;lt;build&amp;amp;gt
    &amp;lt;pluginManagement&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;jaxb2-maven-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;2.3&amp;lt;/version&amp;gt;
                &amp;lt;dependencies&amp;gt;
                    &amp;lt;dependency&amp;gt;
                        &amp;lt;groupId&amp;gt;be.redlab.jaxb&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;swagger-jaxb&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;1.5-SNAPSHOT&amp;lt;/version&amp;gt;
                    &amp;lt;/dependency&amp;gt;
                    &amp;lt;dependency&amp;gt;
                        &amp;lt;groupId&amp;gt;javax.xml.parsers&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;jaxp-api&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;1.4.5&amp;lt;/version&amp;gt;
                    &amp;lt;/dependency&amp;gt;
                    &amp;lt;dependency&amp;gt;
                        &amp;lt;groupId&amp;gt;com.sun.xml.parsers&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;jaxp-ri&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;1.4.5&amp;lt;/version&amp;gt;
                    &amp;lt;/dependency&amp;gt;
                    &amp;lt;dependency&amp;gt;
                        &amp;lt;groupId&amp;gt;com.sun.xml.bind&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;jaxb-xjc&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;2.2.11&amp;lt;/version&amp;gt;
                    &amp;lt;/dependency&amp;gt;
                    &amp;lt;dependency&amp;gt;
                        &amp;lt;groupId&amp;gt;com.sun.xml.bind&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;jaxb-core&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;2.2.11&amp;lt;/version&amp;gt;
                    &amp;lt;/dependency&amp;gt;
                &amp;lt;/dependencies&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/pluginManagement&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;jaxb2-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;2.3&amp;lt;/version&amp;gt;
            &amp;lt;executions&amp;gt;
                    &amp;lt;execution&amp;gt;
                        &amp;lt;id&amp;gt;internal.generate&amp;lt;/id&amp;gt;
                        &amp;lt;goals&amp;gt;
                            &amp;lt;goal&amp;gt;xjc&amp;lt;/goal&amp;gt;
                        &amp;lt;/goals&amp;gt;
                        &amp;lt;configuration&amp;gt;
                            &amp;lt;arguments&amp;gt;-swaggerify&amp;lt;/arguments&amp;gt;
                            &amp;lt;clearOutputDir&amp;gt;true&amp;lt;/clearOutputDir&amp;gt;
                            &amp;lt;packageName&amp;gt;be.redlab.jaxb.swagger.generated.model&amp;lt;/packageName&amp;gt;
                            &amp;lt;sources&amp;gt;
                                &amp;lt;source&amp;gt;${project.basedir}/src/main/xsd/schema&amp;lt;/source&amp;gt;
                            &amp;lt;/sources&amp;gt;
                        &amp;lt;/configuration&amp;gt;
                    &amp;lt;/execution&amp;gt;
            &amp;lt;/executions&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Andreas-Maier-NTT/swagger-jaxb"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This one deals with a lot of my concerns and does its job but I was still not satisfied with the solution, so I forked this repository and made my changes on it.&lt;/p&gt;

&lt;h1&gt;
  
  
  The contribution
&lt;/h1&gt;

&lt;p&gt;Here is the repository with my changes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nandorholozsnyak"&gt;
        nandorholozsnyak
      &lt;/a&gt; / &lt;a href="https://github.com/nandorholozsnyak/swagger-jaxb"&gt;
        swagger-jaxb
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JAXB XJC Plugin for automatically adding annotations from Swagger to generated classes from an XSD
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
swagger-jaxb&lt;/h1&gt;
&lt;p&gt;JAXB XJC Plugin for automatically adding annotations from Swagger to generated classes from an XSD&lt;/p&gt;
&lt;p&gt;Tests run in separate project, see here for the code &lt;a href="https://github.com/redlab/swagger-jaxb-tck"&gt;https://github.com/redlab/swagger-jaxb-tck&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
What does it do?&lt;/h1&gt;
&lt;p&gt;When you are using XSD to generate your DTO classes you may want to annotate them to make it as detailed as possible for the clients whose are going to use your endpoints.&lt;br&gt;
The XSD to Java classes generation can be extended with this tool to make your API objects as declarative as they can be.&lt;br&gt;
The generated classes and methods will be annotated with the proper Swagger annotation, classes with &lt;code&gt;@ApiModel&lt;/code&gt; and methods with &lt;code&gt;@ApiModelProperty&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;
Example&lt;/h1&gt;
&lt;p&gt;Example XSD object:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt; &amp;lt;xsd:complexType name="LoginRequestType"&amp;amp;gt
        &amp;lt;xsd:annotation&amp;gt;
            &amp;lt;xsd:documentation xml:lang="en"&amp;gt;Login object containing the e-mail and password&amp;lt;/xsd:documentation&amp;gt;
        &amp;lt;/xsd:annotation&amp;gt;
        &amp;lt;xsd:complexContent&amp;gt;
                &amp;lt;xsd:sequence&amp;gt;
                    &amp;lt;xsd:element name="email" type="xsd:string"&amp;gt;
                        &amp;lt;xsd:annotation&amp;gt;
                            &amp;lt;xsd:documentation xml:lang="en"&amp;gt;E-mail of the user&amp;lt;/xsd:documentation&amp;gt;
                        &amp;lt;/xsd:annotation&amp;gt;
                    &amp;lt;/xsd:element&amp;gt;
                    &amp;lt;xsd:element name="password" type="xsd:string"&amp;gt;
                        &amp;lt;xsd:annotation&amp;gt;
                            &amp;lt;xsd:documentation xml:lang="en"&amp;gt;Password of the user with SHA512 encoding&amp;lt;/xsd:documentation&amp;gt;
                        &amp;lt;/xsd:annotation&amp;gt;&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nandorholozsnyak/swagger-jaxb"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A detailed README.MD and some examples are provided, most of the features are covered by unit tests, but not all, the codes by redlab and by Andreas were a bit reworked but not all.&lt;/p&gt;

&lt;p&gt;In my projects right now I use &lt;a href="https://github.com/springfox/springfox"&gt;Springfox&lt;/a&gt; for Swagger functionality and with this little plugin all of my DTOs which take part in the communication with the clients are detailed.&lt;/p&gt;

&lt;p&gt;If you are interested in a extended XSD2Java class generation with Swagger then this is your time to pull this dependency into your &lt;strong&gt;pom.xml&lt;/strong&gt; and start generating :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Thank you for everybody who read my first blog post, I really appreciate it, and because I'm really new to writing I'm eager for some feedback in the comment section.&lt;/p&gt;

&lt;p&gt;I'm curious if anybody is still using XSD schemas to generate their Java classes or maybe JSON schemas took over (btw I validate the incoming request against these schemas, so there is no surprise in the business logic ;))&lt;/p&gt;

&lt;p&gt;Let me know what do you guys think about that and feel free to open issues or PR's.&lt;/p&gt;

&lt;p&gt;I try to maintain the code, the code quality is not the best, I'm still wondering on a lof of refactor options but for know, it is okay to use, as I will use it from now ;)&lt;/p&gt;

</description>
      <category>java</category>
      <category>swagger</category>
      <category>xsd</category>
      <category>jaxb</category>
    </item>
  </channel>
</rss>
