Packaging Java apps with Maven and GitHub Actions
Tags: java, maven, github, devops
Published: 2021-11-20 15:27:39

This post shows how to create workflows that package Java application with Maven and then store it as an artifact or publish to GitHub Packages.

In the previous [post](https://dev.to/cvitaa11/continuous-integration-with-github-actions-2mo5) we described GitHub Actions and how they work, so if you need a quick reminder on the jobs, steps and syntax check it out.

### Development environment

On the following [link](https://github.com/cvitaa11/java-spring-demo) you can find repository with the source code. It's a simple Spring Boot application that returns students from the database. The application was bootstrapped using Spring Initializr. For the dependencies we added `Spring Web` which is used for building web applications using Spring MVC. This package also uses Apache Tomcat as the default embedded container. We also used `Spring Data JDBC` for persisting data in SQL with plain JDBC and `PostgreSQL Driver` that allows Java programs to connect to a Postgres database using standard, database independent Java code.

For local development, we can easily start Postgres instance with Docker by using the following command: 
```
docker run -d --name postgresDB -p <port>:5432 -e POSTGRES_PASSWORD=<YourPassword>-v /postgresdata:/var/lib/postgresql/data postgres:latest
```

Spring Boot follows a layered architecture in which each layer communicates with the layer directly below or above it. We followed that practice and implemented Controller, Service and Repository inside our application and demonstrated dependency injection principles. Source code also contains `StudentConfig` class which simply inserts student in database.

After successfully setting up development environment and writing some code we decided to push our work into the source control management system, in this case GitHub. Now we need to build the code and publish it as a Maven package. This process can be done manually, but we would like to do it automatically when changes are merged into the `main` branch. In this way we will avoid manual tasks when publishing a new version.

Like most other things, this problem can be solved in several ways, and we will use two different approaches. Firstly, we will publish our package as an build artifact and make it available for download, while in the second approach we will publish package to the [GitHub Packages](https://github.com/features/packages) Maven repository.

### Storing workflow data as artifacts

In the `main.yaml` file first couple of lines tell us which events will start the workflow. Except push and pull request on the main branch we also added `workflow_dispatch` tag to enable manual workflow run.

Under the jobs section we defined `build` job that will be executed on the Ubuntu runner. First two steps checkout main branch from GitHub and set up JDK (Java Development Kit).

Next up, we build the project and set up a cache for Maven:

```
- name: Build Maven project
  run: |
    mvn -B package --file pom.xml -Dmaven.test.skip
    mkdir staging && cp target/*.jar staging

- name: Set up a cache for Maven
  uses: actions/cache@v2
  with:
    path: ~/.m2
    key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
    restore-keys: ${{ runner.os }}-m2
```

Once the build is completed, staging directory will contain produced .jar file. Each job in a workflow runs in a fresh virtual environment which means that once the `build` job is done we cannot access that environment and our .jar file is gone. That's where our last step comes in place which uploads artifacts from your workflow allowing you to share data between jobs and store data once a workflow is complete. 

```
- name: Persist workflow data as artifacts
  uses: actions/upload-artifact@v2
  with:
    name: github-actions-artifact
    path: staging
```

By default, the artifacts generated by workflows are retained for 90 days before they are automatically deleted. You can adjust the retention period, depending on the type of repository. When you customize the retention period, it only applies to new artifacts and does not retroactively apply to existing objects. 

Artifacts can be found under the Actions tab when you click on the desired workflow run.

![GitHub Actions Artifacts](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpiy1wxdrrw5jyrtfc84.png)

### Publishing to the GitHub Packages

You can configure Apache Maven to publish packages to GitHub Packages and to use packages stored on GitHub Packages as dependencies in a Java project.

Beside Maven, GitHub Packages offers different package registries for commonly used package managers like npm, NuGet, Gradle and RubyGems. It's also possible to store Docker and other OCI images. With all these features you can create end-to-end DevOps solutions and centralize your software development on GitHub.

In `maven-publish.yaml` file you can find workflow details for publishing package to the GitHub Packages. Just like in the previous solution we provide name and events that will trigger workflow run. Next, under the jobs section we selected Ubuntu runner as environment for the `publish` job and defined permissions to read the content and write packages.

```
name: Publish package to GitHub Packages
on:
  push:
    branches: [main]
jobs:
  publish:
    runs-on: ubuntu-latest 
    permissions: 
      contents: read
      packages: write 
```

In the following steps we checkout the main branch and set up JDK with `java-version` parameter. The last step is publishing the package and it needs personal access token for the authentication. PAT is a sensitive information and we don't want to store it as a plain text, so we defined the secret on the repository level and accessed it as an environment variable. For simplicity we skipped the tests during deployment.

```
- name: Publish package
  run: mvn --batch-mode deploy -Dmaven.test.skip
  env:
    GITHUB_TOKEN: ${{ secrets.TOKEN }}
```

Before running the workflow we also need one configuration change in the application source code. Inside `pom.xml` file we need to pass information about package distribution management.

```
<project ...>
  ...
  <distributionManagement>
    <repository>
      <id>github</id>
      <name>GitHub Packages</name>
      <url>https://maven.pkg.github.com/cvitaa11/java-spring-demo</url>
    </repository>
  </distributionManagement>
</project>
```

After pushing changes to the main branch workflow starts automatically and we can follow the log output under the Actions tab on the repository page. When all steps are completed we can see Maven package, ready for use, in our code repository under the Packages section.

![Java repository](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bino2ic60brye9iv39fj.png)

![Maven package](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fgpisr4w0c4lnvtg7uke.png)

With these two approaches, we have successfully solved the problem of automatic publish of Java applications as Maven packages and demonstrated use of GitHub Actions and GitHub Packages to create complete end-to-end development solution in one place.