Installing Maven with chezmoi

I use chezmoi to manage my dotfiles, including a few small scripts that install development tools in my home directory.

One of these scripts installs Apache Maven. I keep it as a run_onchange_after_ script so that chezmoi runs it only when the script changes. This is especially convenient for versioned tools: when I want to upgrade Maven, I change the version number in the script, and the next chezmoi apply installs the new version.

The script is called:

Here is the complete script.

What this script does

The script installs a specific Maven version into my home directory, without requiring root privileges and without relying on a distribution package manager.

The resulting layout is:

The versioned installation lives under ~/.local/opt/apache-maven-<version>, while ~/.local/opt/maven is a stable symbolic link to the currently selected Maven version.

This gives me two useful properties at the same time: I can keep explicit versioned installations, but I can also refer to Maven through a stable path.

Why run_onchange_after_

chezmoi treats scripts with names such as run_onchange_after_20-install-maven.sh specially.

The run_onchange_ part means the script is executed when its contents change. The after part means it runs after chezmoi has applied the rest of the dotfiles.

That is exactly what I want here. Maven does not need to be downloaded on every chezmoi apply. It only needs to be installed when I change something relevant in the script, such as the Maven version.

To upgrade Maven, I change this line:

Then I run:

Since the script content changed, chezmoi runs it again and installs the new Maven version.

Why install under ~/.local

I prefer installing this kind of tool under my home directory:

and exposing commands through:

This avoids using sudo, keeps the installation independent from the operating system package manager, and makes the setup easier to reproduce across machines.

It also avoids interfering with any Maven version installed system-wide. The Maven installed by this script is simply the one that appears first in my PATH.

Downloading from Apache mirrors

The script constructs the Maven download URL based on the configured version. For current Maven releases, it downloads from the main Apache download infrastructure.

There is also a fallback to the Apache archive. This matters because older versions are eventually removed from the main mirrors. Since dotfiles often pin specific versions for a long time, falling back to the archive makes the script more robust.

This is useful when setting up a new machine months later: even if the pinned Maven version is no longer on the primary mirror, the script can still find it in the Apache archive.

Verifying the archive

The script downloads both the Maven archive and the corresponding SHA-512 checksum file.

Before extracting Maven, it computes the local SHA-512 digest of the downloaded archive and compares it with the published checksum. If the two values do not match, the script stops immediately.

The checksum parsing is intentionally tolerant: instead of depending on one exact checksum-file format, the script scans the file for a 128-character hexadecimal token, which corresponds to a SHA-512 digest.

For computing the local checksum, the script supports several common tools:

  • sha512sum
  • shasum
  • openssl

This makes the script usable on different Unix-like systems without assuming one specific checksum command.

Idempotency

The script is safe to run more than once.

If the requested Maven version is already installed and contains an executable mvn, the script does not download or extract it again. It still refreshes the stable symlink and the command links, but it avoids unnecessary network access.

When installation is needed, everything is done first in a temporary directory. The archive is downloaded there, the checksum is verified there, and Maven is extracted there. Only after the extracted archive looks valid is it moved into the final installation directory.

That keeps partially downloaded or invalid archives away from the final destination.

Stable symlinks

The script creates a stable Maven symlink:

pointing to the selected versioned installation:

Then it creates command symlinks in ~/.local/bin:

These point through the stable Maven symlink instead of directly to the versioned directory.

That means the command links do not need to change structurally when Maven is upgraded. The maven symlink changes, and mvn follows it.

The script is also conservative: it overwrites existing symlinks, but it does not overwrite existing non-symlink files. If a real file or directory already exists where the script wants to create a symlink, the script either keeps it or stops with an error.

PATH handling

At the end, the script checks whether ~/.local/bin is already in PATH.

If it is not, the script does not automatically modify shell startup files. It simply prints a reminder to add:

I prefer this because installation scripts should not unexpectedly edit shell configuration files. The dotfiles themselves are the right place to manage PATH.

Why not use the package manager?

Installing Maven from the system package manager is perfectly fine in many cases.

For my dotfiles, however, I prefer this script because it gives me:

  • a specific upstream Maven version;
  • the same version across different machines;
  • a user-local installation;
  • no dependency on distribution-specific package names;
  • a simple upgrade mechanism through chezmoi;
  • checksum verification before installation.

This approach is not meant to replace the package manager for everything. It is just a good fit for development tools where I care about the exact upstream version.

Final result

After running:

Maven is available as:

and the installation is fully contained under my home directory.

The important paths are:

This keeps the setup explicit, reproducible, and easy to update across machines.

Here’s an example updating the dotfiles on a machine after updating the Maven version:

Happy dotfiles! 😉

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.