TL;DR
If you use Gradle build for Micronaut, configure shadowJar to output file named app.jar so default settings will work when deploying app via DevOps on Azure. If you won't use Azure DevOps to deploy Micronaut applications no point of reading further, nothing to see here, move on.
Intro
Recently I was required to setup a backend project. I chose Java for ecosystem reason and Micronaut because it's supposed to be fast and it comes with a lot of tools to ease development for Cloud. While creating app I decided on Gradle build tool. The problem was requirement was Azure. I thought well there's a lot of instructions, tutorials and such so it should not be a problem. I also wanted to deploy to existing App Service using Linux through Azure DevOps. So point is I want to press Approve on a pull request and it should build it, deploy it, and then it should be available online for testing.
Hidden problem
First problem was getting any kind of info on how to automate Java Gradle project . After it "worked", I thought OK, this is it, now I just need to setup deployment. I struggled with getting the actual .jar file deployed anywhere and how to set it up with App Services.
Basic steps in DevOps build pipeline were:
- Setup steps so that the gradlew gets ran with assemble task
- Copy jar to target location that is: $(Build.ArtifactStagingDirectory)
- Publish artifacts to existing App Services that uses Linux and has Java environment
Natural lead would be to setup Release pipeline to:
- Have correct subscription - same as where the App Service resource is located
- Setup start command on the same config part (where you set the stage name)
- For example if output of build process was myApp.jar, starting command would be java -jar myApp.jar.
- Have Agent job deploy correct artifacts.
- Use Azure App Service deploy to get the artifact to App Service created earlier
Now I spent too much time to find out why this wasn't working.
Microsoft has successfully hidden some facts inside of documentations under different title that one would expect things to be. And of course expected ways of thing working with DevOps and App Service were not default.
It appears they expect jars to be named app.jar. Huh...
Name the file app.jar
If you search Java App Service Linux you'll get a lot Spring Boot stuff with tutorials for maven. On the other hand Micronaut Auzre App Services returns mostly Micronaut tutorial for Azure CLI. I din't want to switch to maven, nor use spring boot, nor use CLI and what not.
I knew there had to be a way but it's painful if MS doesn't at least try to make basic things available for other than Spring or Azure CLI way.
So I stumble upon this link. At the end you can find that they actually have a place that says what is required for default settings: Jar deployed to App Service through DevOps pipelines should be named app.jar.
Now I tried setting up the start command to avoid rename, but I guess I got the path wrong which kept me in the loop a bit longer.
Also App Services, once Linux with JavaSE is selected, have preset jar file which runs on vm boot. So if you try to debug and jump in web shell provided by Azure, and run the application manually, it collides with the current running app because of the port and crashes the shell (probably even the vm). I gets back running by itself automatically later on.
So I guess easiest way is to force deployment to create app.jar
But I didn't want to name everything app.
shadowJar
Micronaut uses shadowJar plugin for fatJar. Here is specified on how to change only the name of the output and default way of naming things.
Output name for jar
So if you start Gradle assemble task or any other task that makes the build you will get 2 jar by default. One is name.jar other is name-version.jar. This is of course if you specify all the fields when creating Micronaut app like version, name, group...
I search for a solution on how to change Gradle output name for Java usually ending with maven mostly and deprecated stuff for Gradle. I tried Gradle attributes for jar plugin but it didn't help. At the end I finally realised that shadowJar is the one actually used and it should be configured instead of java plugin.
Wrong attributes
While it has a nice documentation it is a bit deprecated. I got myself Gradle 5.5 version (and later updated to 5.6.2.) and shadowJar 5.1 (I think, it was latest at that time). When trying to run build I got warnings and unexpected output for filename. It generated wrong names and reported deprecated on baseName property inside of shadowJar config. Took me quite some time to find how to rename output on Gradle and now I got this.
Well at least that one was quicker as it appears it should be archiveFileName instead of any other I tried. So build.gradle should contain something like this:
shadowJar {
archiveFileName="app.jar"
mergeServiceFiles()
}
Took me a couple of minutes to wait for the deployment and viola it worked.
Ending
Well it took me a whole work day to set this up. I'm not proud nor specially happy just glad that it's over. It would be useful if the app.jar rule was exposed a bit more in documentations, at least I think so.
Top comments (0)