BuildGradleApplication
A Nix builder function for packaging Gradle applications.
Install / Use
/learn @raphiz/BuildGradleApplicationREADME
buildGradleApplication
buildGradleApplication is a Nix builder function for packaging Gradle applications.
buildGradleApplication drastically simplifies the integration of Gradle projects in the Nix ecosystem by defining a set of rules/constraints that a Gradle project must follow.
Goals
- For now, the focus is on packaging Gradle applications, not libraries.
- Using the builder function should feel idiomatic to Nix. It should provide the same experience as
buildPythonPackageorbuildPerlPackagebut with fewer options. - The rules imposed on the Gradle build should be idiomatic to Gradle and ideally promote Gradle best practices.
- Support automatic updates with tools such as renovate.
- All dependencies (jars) should be packaged into discrete derivations (and linked in the final result) to facilitate efficient deployments and layered OIC images.
- This project should be small and simple.
Non-Goals
buildGradleApplicationis not a general purpose solution for building arbitrary Gradle projects. If you want to do that, check out gradle2nix instead.- Do not try to replicate Gradle's behaviour, e.g. to construct a runtime classpath. Instead, use the Gradle built-ins to produce these results.
- Android. But if you have experience in Android, talk to me! It might not be that hard to support android instead (by breaking/adopting Rule #5).
Installation (via flakes)
{
inputs = {
build-gradle-application.url = "github:raphiz/buildGradleApplication";
# ...
};
# ...
outputs = {
nixpkgs,
build-gradle-application,
...
}: {
# ...
pkgs = import nixpkgs {
inherit system;
overlays = [build-gradle-application.overlays.default];
};
# ...
};
}
Usage
The usage of buildGradleApplication should be straight forward once your build follows the outlined rules below. Here is a very minimal example:
# package.nix
{
lib,
version,
buildGradleApplication,
}:
buildGradleApplication {
pname = "hello-world";
version = version;
src = ./.;
meta = with lib; {
description = "Hello World Application";
};
}
For further examples, checkout the examples directory
All available parameters of buildGradleApplication are documented in the source code
Rules
Rule #1: Requires Checksum Verification (verification-metadata.xml)
Using Gradle's built-in Mechanism for dependency verification is not only a security best practice, but also allows buildGradleApplication to fetch an fixed version (as a fixed-output derivations) of a dependency and its metadata.
While it should be straight forward to generate a verification-metadata.xml file by following the documentation, take extra care that Gradle version and JDK version align! This should not be a problem when using Nix for your development environment.
Here is an example command to let Gradle add all dependency artifacts to your verification-metadata.xml:
gradle --refresh-dependencies --write-verification-metadata sha256 --write-locks dependencies
Gradle does not remove any artefacts from the verification-metadata.xml even if they are not used anymore. This can lead to a unnecessary large file. The updateVerificationMetadata package from this flake can be used to re-generate the file while keeping the <configuration> section. Again: You must ensure that the Gradle version and JDK version align.
update-verification-metadata
Tip: Renovate can and will append updated dependencies to this file - Yay 🎉
Dependency Verification and IntelliJ IDEA
Gradle's verification-metadata.xml file enforces that only explicitly listed artifacts are downloaded during builds. However, this can lead to issues when using IDEs like IntelliJ IDEA, which will download additional artifacts (Javadoc, source files and more) that are not included in the verification metadata.
To handle this issue, you have two options. The one you choose depends on how important dependency verification is to you compared to the effort required to maintain it:
Option 1: Disable Dependency Verification for Development (low effort)
Simplify the development process by disabling Gradle's dependency verification. Add the following line to your gradle.properties file:
org.gradle.dependency.verification=off
Note: You still need the verification-metadata.xml file to download the required artifacts and build the Nix package. However, disabling dependency verification prevents you from having to deal with these quirks during development.
2. Manually Add Missing Dependencies (more secure when done properly)
Manually identify and add additional dependencies required by IntelliJ IDEA into the verification-metadata.xml file. I use a script to simplify that.
2b. Automatically Trust Javadoc and Source Artifacts
Update your verification-metadata.xml file to automatically trust Javadoc and source files, allowing IntelliJ IDEA to fetch them without verification errors. Here's an example configuration:
<!-- gradle/verification-metadata.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<verification-metadata xmlns="https://schema.gradle.org/dependency-verification" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
<configuration>
<verify-metadata>true</verify-metadata>
<verify-signatures>false</verify-signatures>
<trusted-artifacts>
<!-- See https://youtrack.jetbrains.com/issue/IDEA-258328 -->
<trust file=".*-javadoc[.]jar" regex="true"/>
<trust file=".*-sources[.]jar" regex="true"/>
<trust file="gradle-[0-9.]+-src.zip" regex="true"/>
<trust file="groovy-[a-z]*-?[0-9.]+.pom" regex="true"/>
</trusted-artifacts>
</configuration>
<components>
<!-- Define other dependencies here -->
</components>
</verification-metadata>
Rule #2: Maven Repositories Only
buildGradleApplication only supports Maven repositories to fetch dependencies. Ivy is not supported.
Rule #3: No Downloads
Nix uses a sandbox which prevents internet access during build time (for a good reason). All other (implicit) build dependencies must be provided via Nix instead. buildGradleApplication takes care of downloading and providing the Maven dependencies. Everything else is specific to your build and must be handled by you.
Let's take the gradle-node plugin as an example. It can be configured to download and install a specific version of Node.js. This will fail for the reason given above. Instead, provide Node.js as nativeBuildInput instead:
buildGradleApplication {
# ...
nativeBuildInputs = [pkgs.nodejs];
}
Rule #4: Centralized Repository
Because Nix uses a sandbox which prevents internet access during build time, buildGradleApplication needs to pre fetch all required artifacts. These are then made available to the offline build using a local maven repository. The location of this repository depends on it's contents and is provided to the Gradle build via the MAVEN_SOURCE_REPOSITORY Environment Variable.
It's a Gradle best practice to centralize repositories declarations.
buildGradleApplication assumes that all repository declarations are located in your settings.gradle(.kts) files. It will then replace these declarations during build time with the location of the offline repository (using a Gradle init script)
Here is an example of how your Gradle build should declare it's repositories:
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
}
// Highly recommended, see https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:centralized-repository-declaration
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
Note that repository declarations must be defined for each included build as well.
Also Note that buildGradleApplication is (currently) unable to extract the declared repositories from your Gradle build. If you use different or additional repositories, you must provide it to buildGradleApplication using the repositories parameter:
buildGradleApplication {
# ...
repositories = ["https://plugins.gradle.org/m2/" "https://repo1.maven.org/maven2/" "https://example.com/maven2/"];
}
Rule #5: Using the application Plugin
Currently, the focus of this tool is to package Gradle applications. In order to launch a java application,
