TNS
VOXPOP
Do You Resent AI?
If you’re a developer, do you resent generative AI’s ability to write code?
Yes, because I spent a lot of time learning how to code.
0%
Yes, because I fear that employers will replace me and/or my peers with it.
0%
Yes, because too much investment is going to AI at the expense of other needs.
0%
No, because it makes too many programming mistakes.
0%
No, because it can’t replace what I do.
0%
No, because it is a tool that will help me be more productive.
0%
No, I am a highly evolved being and resent nothing.
0%
I don’t think much about AI.
0%
CI/CD / DevOps

How To Build With GraalVM Inside GitHub Actions

Set up CI/CD pipelines to build your Java applications for macOS, Linux or Windows with GraalVM.
May 14th, 2024 9:10am by
Featued image for: How To Build With GraalVM Inside GitHub Actions
Featured image by Maria Teneva on Unsplash.

GitHub Actions is a CI/CD platform that enables project developers on GitHub to automate their build, test and deployment pipelines. GraalVM is an advanced Java Development Kit (JDK) with ahead-of-time Native Image compilation. Not only does this make it easy to produce executables for different platforms, but it also keeps your development cycle nice and short because you don’t need to run any builds on your developer machine when making changes. More than 2,000 open source projects are already doing this — you can too!

The GraalVM JDK is becoming more and more popular in the Java community, not just because of its high-performance just-in-time (JIT) compiler but also because of its ahead-of-time Native Image compilation. Java microservices frameworks such as Spring, Micronaut, Quarkus and Helidon have added first-class support for GraalVM, enabling users to turn their microservices into executables that are efficient, fast, more secure and easy to deploy. And the list of Java libraries and frameworks tested with Native Image is constantly growing.

Many users are excited about GraalVM because the executables it generates have a small memory footprint without compromising on performance, provide extremely fast startup times, have improved security thanks to a reduced attack surface, are self-contained, and are also cheaper and easier to deploy when compared to conventional jlink-based deployments. GraalVM provides a lot of benefits for Java developers, but it also comes with some costs.

“Write Once, Run Anywhere” and Slow Builds

Java gained a lot of popularity as a programming language because of its portability. The GraalVM JDK still provides portability, but the native executables it generates are platform-specific. This may be an obvious difference between JIT and ahead-of-time (AOT) compilation. For Java developers, however, it’s a paradigm shift: Instead of distributing a portable JAR file, developers now need to generate and distribute a different executable for each platform on which their application runs. Fortunately, microservices are often deployed on Linux servers using the same or similar hardware. Developers of command-line interface tools, on the other hand, often want to provide executables for their macOS, Linux and Windows users.

Fast startup times are one of the benefits of native executables. They only start-up fast because AOT compilation shifts work from run time (when the application runs) to build time (when the application is built). This includes JDK and application startup (the time it takes to initialize the JDK standard library and the application), which can slow down applications significantly, especially when they are short-running. (The One Billion Row Challenge demonstrated this nicely.) GraalVM, however, has to statically analyze and AOT-compile not only the user application but also its dependencies and the majority of the JDK from scratch. Even though this happens within seconds, it’s still not as fast as incrementally compiling a Java application with the javac command.

Compiling an application after every change (and potentially for several different platforms) can obviously be time-consuming, and unnecessarily slows down the development cycle. CI/CD can help with this.

GitHub Actions to the Rescue!

GitHub Actions is one of many GitHub features, and it provides open source projects with free GitHub-hosted runners. Recently, the resources these runners provide have been doubled, and GitHub also added new M1 macOS runners.

To use GitHub Actions, users just have to create a workflow that defines one or more jobs. These jobs can consist of multiple steps, which in turn can use so-called actions. You can find more than 22,000 actions in the GitHub Marketplace, which also includes a GitHub Action for GraalVM called setup-graalvm.

The best way to show how GraalVM can be used inside GitHub Actions is with an example. Get started by creating a main.yml file in the .github/workflows directory of a repository with the following contents:

The example is fully self-contained, defines a new workflow called “GraalVM Native Image builds” and uses GraalVM to compile a HelloWorld application on macOS, Windows and Ubuntu. The on: [push, pull_request, workflow_dispatch]key-value pair instructs GitHub Actions to run the workflow on every git push, on pull requests and manually.

To run on different platforms, define a simple build matrix. MacOS 14 provides the new M1 runners, enabling users to generate native executables for Apple’s latest hardware architecture. For Windows, use Windows Server 2022 because GraalVM Native Image requires Visual Studio 2022 or later. And for Linux, use Ubuntu 20.04, the oldest Ubuntu version supported by GitHub, to maximize compatibility with other Linux systems.

The steps of the build job are as follows:

  1. Checking out the repository is typically the first step you would want to do. For this example, however, it is not needed because the HelloWorld application is defined inline in the workflow file.
  2. Use the GitHub Action for GraalVM to set up Oracle GraalVM for JDK 21, which is then added to the $PATH. The github-token is optional but helps the action reduce rate-limiting issues when talking to the GitHub API. native-image-job-reports is one of many other options supported by the actions. More on that later.
  3. Create a HelloWorld.java file and compile it with javac. Instead of running it with java HelloWorld, use native-image HelloWorld to compile the application into a native executable. The last line of this step runs the executable. Upload the native executable as an artifact of each job. Alternatively, you could also make a GitHub release — for example, by using this action — or deploy the executable to your server or preferred cloud service.

Look at the result of this example in action (no pun intended):

Build summary for the HelloWorld example.

Build summary for the HelloWorld example.

In the Actions tab of your repository, you’ll find individual entries for each build of all your workflows. The screenshot above shows the first build, when you created the main.yml file. All three build jobs passed within 2 minutes 13 seconds, and they produced three artifacts in total. These artifacts can easily be downloaded, downloaded and reused in other workflows, or deleted from the build. From this tab, you can also click on any build job, which provides more details, including the build log.

Build log for the HelloWorld job running on ubuntu-20.04.

Build log for the HelloWorld job running on ubuntu-20.04.

A build log can be quite long, especially for more complex projects. This is where the native-image-job-reports option comes into play. It instructs setup-graalvm to produce a build report and attach it as a job summary. Such summaries are shown underneath the list of artifacts in the build summary. Here’s the corresponding report for the job running on ubuntu-20.04:

Build report for the HelloWorld job running on ubuntu-20.04.

Build report for the HelloWorld job running on ubuntu-20.04.

The build report shows that it took 48 seconds to produce a native executable for the HelloWorld application. For this, the job used Oracle GraalVM in the default configuration with optimization level 2 (-O2 ) and machine-learning inferred profile-guided optimization (PGO). The static analysis found more than 2,000 types reachable for the HelloWorld application, and the produced executable’s file size is 7.79MB in total. The build used around 814MB of memory, and it ran on one of the upgraded runners with 16GB of system memory and four CPU cores. The report also provides plenty of links to the documentation with more information.

Finally, there also is an option called native-image-pr-reports to generate build reports. Instead of adding them as job summaries, they are automatically added as comments on pull requests. This makes it easy, for example, to spot improvements or regressions when upgrading dependencies or integrating new features.

Setting Up Your Project

The HelloWorld example illustrates how GraalVM can be used inside GitHub Actions. Real-world Java projects are typically much more complex, but setting them up is often surprisingly simple, especially if you are using one of the microservices frameworks with built-in GraalVM support. In this case, all you typically need to do is run a Maven or Gradle command to build the native executable:

If you don’t use a framework with GraalVM support, but do use Maven or Gradle, you can add the Maven plug-in or Gradle plug-in for GraalVM Native Build Tools to your project configuration. Then you should be able to build with the same commands as above. The plug-ins also help you test a native version of your application. If you build your application manually with javac, simply use native-image as a drop-in replacement for the java command that would otherwise run your application, as in the HelloWorld example.

Conclusion

If you are just as excited about GraalVM as many others in the Java community, consider setting up CI/CD pipelines to build your application with GraalVM for you, and on different platforms!

By using GraalVM inside GitHub Actions, you can test your executable before either uploading it as a build artifact or deploying it directly to your server or cloud service. This way, you don’t need to run any builds locally, nor do you need access to different machines. And for open source projects, both the new Oracle GraalVM distribution and GitHub Actions are free to use.

If you have any suggestions, new ideas or use cases, please feel free to reach out to us on SlackGitHub or X/Twitter.

And now, happy building with GraalVM! 🚀

Group Created with Sketch.
TNS owner Insight Partners is an investor in: Real.
TNS DAILY NEWSLETTER Receive a free roundup of the most recent TNS articles in your inbox each day.