Skip to content

codejive/java-jpm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

186 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jpm - Java Package Manager

A simple command line tool to manage Maven dependencies for Java projects that are not using build systems like Maven or Gradle.

It takes inspiration from Node's npm but is more focused on managing dependencies and is not a build tool. Keep using Maven and Gradle for that. This tool is ideal for those who want to compile and run Java code directly without making their lives difficult the moment they want to start using dependencies.

asciicast

Example

TL;DR

$ jpm install com.github.lalyos:jfiglet:0.0.9
Artifacts new: 1, updated: 0, deleted: 0
$ java -cp deps/* HelloWorld.java
  _   _      _ _         __        __         _     _ _
 | | | | ___| | | ___    \ \      / /__  _ __| | __| | |
 | |_| |/ _ \ | |/ _ \    \ \ /\ / / _ \| '__| |/ _` | |
 |  _  |  __/ | | (_) |    \ V  V / (_) | |  | | (_| |_|
 |_| |_|\___|_|_|\___( )    \_/\_/ \___/|_|  |_|\__,_(_)

Slightly longer explanation:

Imagine you're writing a simple Java console command, and you want to use JFigletFont for some more impactful visuals. You've written the following code:

import com.github.lalyos.jfiglet.FigletFont;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        System.out.println(FigletFont.convertOneLine("Hello, World!"));
    }
}

But now you get to the point that you need to add the JFigletFont library to your project. You could start using Maven or Gradle, but that seems overkill for such a simple project. Instead, you can use jpm. First you can search for the library if you can't remember the exact name and version:

$ jpm search jfiglet
com.github.dtmo.jfiglet:jfiglet:1.0.1
com.github.lalyos:jfiglet:0.0.9

So let's install the second library from that list:

$ jpm install com.github.lalyos:jfiglet:0.0.9
Artifacts new: 1, updated: 0, deleted: 0

Let's see what that did:

$ tree
.
├── app.yml
├── deps
│   └── jfiglet-0.0.9.jar -> /home/user/.m2/repository/com/github/lalyos/jfiglet/0.0.9/jfiglet-0.0.9.jar
└── HelloWorld.java

As you can see jpm has created a deps directory and copied the JFigletFont library there (although in fact it didn't actually copy the library itself, but instead it created a symbolic link to save space).

We can now simply run the program like this (using Java 11 or newer):

$ java -cp "deps/*" HelloWorld.java
  _   _      _ _         __        __         _     _ _
 | | | | ___| | | ___    \ \      / /__  _ __| | __| | |
 | |_| |/ _ \ | |/ _ \    \ \ /\ / / _ \| '__| |/ _` | |
 |  _  |  __/ | | (_) |    \ V  V / (_) | |  | | (_| |_|
 |_| |_|\___|_|_|\___( )    \_/\_/ \___/|_|  |_|\__,_(_)

But if we look again at the above output of tree, we also see an app.yml file. This file is used by jpm to keep track of the dependencies of your project. If you want to share your project with someone else, you can simply share the app.yml file along with the code, and they can run jpm install to get the required dependencies to run the code.

NB: We could have used jpm copy instead of jpm install to copy the dependencies but that would not have created the app.yml file.

Installation

For now the simplest way to install jpm is to use JBang:

jbang app install jpm@codejive

But you can also simply download and unzip the release package and run the bin/jpm script.

Dependencies

Dependencies are listed in the dependencies section of the app.yml file:

dependencies:
  com.github.lalyos:jfiglet: 0.0.9
  info.picocli:picocli: 4.7.7
  org.yaml:snakeyaml: '2.5'

Actions

The app.yml file doesn't just track dependencies - it can also define custom actions that can be executed with the jpm do command or through convenient alias commands.

Defining Actions

Actions are defined in the actions section of the app.yml file:

dependencies:
  com.github.lalyos:jfiglet:0.0.9

actions:
  setup: "echo Do some actual work here..."
  build: "javac -cp {{deps}} *.java"
  run: "java -cp {{deps}} HelloWorld"
  test: "java -cp {{deps}} TestRunner"
  it: "java -cp {{deps}} IntegrationTestRunner"
  clean: "rm -f *.class"

Executing Actions

You can execute actions using the jpm do command:

$ jpm do --list                 # Lists all available actions
$ jpm do build                  # Runs the build action
$ jpm do run --arg foo -a bar   # Passes "foo" and "bar" to the run action
$ jpm do build -a --verbose run -a fubar   # Passes "--verbose" to build and "fubar" to run

Or use the convenient alias commands that exist especially for "clean", "build", "test" and "run":

$ jpm build        # Executes the 'build' action
$ jpm run          # Executes the 'run' action
$ jpm test         # Executes the 'test' action
$ jpm clean        # Executes the 'clean' action

Alias commands can accept additional arguments that will be passed through to the underlying action:

$ jpm run --verbose debug    # Passes '--verbose debug' to the run action

Variable Substitution

Actions support several variable substitution features for cross-platform compatibility:

  • {{deps}} - Replaced with the full classpath of all dependencies
  • {/} - Replaced with the file separator (\ on Windows, / on Linux/Mac)
  • {:} - Replaced with the path separator (; on Windows, : on Linux/Mac)
  • {~} - Replaced with the user's home directory (The actual path on Windows, ~ on Linux/Mac)
  • {./path/to/file} - Converts relative paths to platform-specific format
  • {./libs:./ext:~/usrlibs} - Converts entire class paths to platform-specific format
  • {;} - For use with multi-command actions (& on Windows, ; on Linux/Mac). Really not that useful, you can use && instead which works on all platforms

Example with cross-platform compatibility:

actions:
  build: "javac -cp {{deps}} -d {./target/classes} src{/}*.java"
  run: "java -cp {{deps}}{:}{./target/classes} Main"
  test: "java -cp {{deps}}{:}{./target/classes} org.junit.runner.JUnitCore TestSuite"

NB: The {{deps}} variable substitution is only performed when needed - if your action doesn't contain {{deps}}, jpm won't resolve the classpath, making execution faster for simple actions that don't require dependencies.

NB2: These actions are just a very simple convenience feature. For a much more full-featured cross-platform action runner I recommend taking a look at:

  • Just - Just a command runner

Usage

Main Command

Usage: jpm [-hvV] [COMMAND]

Options:
  -h, --help      Show this help message and exit.
  -v, --verbose   Enable verbose output for debugging
  -V, --version   Print version information and exit.

Commands:
  search   Search for Maven artifacts in repositories.
  install  Install artifacts and add them to app.yml dependencies.
  copy     Copy artifacts to a directory without modifying app.yml.
  path     Print the classpath for the specified artifacts or app.yml dependencies.
  do       Execute an action defined in app.yml.
  exec     Execute a shell command.

Common options

These options are supported by all commands:

      --config=<configFile>
                        Path to user configuration file (default: JPM_CONFIG
                        environment variable, ~/.config/jpm/config.yml, or
                        ~/.jpmcfg.yml)
  -c, --cache=<cacheDir>
                        Directory where downloaded artifacts will be cached
                        (default: value of JPM_CACHE environment variable;
                        whatever is set in Maven's settings.xml or
                        $HOME/.m2/repository
  -d, --directory=<directory>
                        Directory to copy artifacts to
  -L, --no-links        Always copy artifacts, don't try to create symlinks
  -r, --repo=<repositories>
                        URL to additional repository to use when resolving
                        artifacts. Can be preceded by a name and an equals
                        sign, e.g. -r myrepo=https://my.repo.com/maven2.
                        When needing to pass user and password you can set
                        JPM_REPO_<name>_USER and JPM_REPO_<name>_PASSWORD
                        environment variables.
  -q, --quiet           Don't output non-essential information
  -v, --verbose         Enable verbose output for debugging

Commands

search (alias: s)

Search for Maven artifacts in repositories.

Usage: jpm search [-iLqv] [-a=<appInfoFile>] [-b=<backend>] [-c=<cacheDir>]
                  [-d=<directory>] [-m=<max>] [-r=<repositories>]...
                  artifactPattern

Parameters:
  artifactPattern       Partial or full artifact name to search for.

Options:
  -i, --interactive     Interactively search and select artifacts to install
  -b, --backend=<backend>
                        The search backend to use. Supported values:
                          rest_smo, rest_csc
  -m, --max=<max>       Maximum number of results to return
  -a, --appinfo=<appInfoFile>
                        App info file to use (default './app.yml')

Example:
  jpm search httpclient

install (alias: i)

Install artifacts and add them to app.yml dependencies.

Usage: jpm install [-Lqv] [-a=<appInfoFile>] [-c=<cacheDir>] [-d=<directory>]
                   [-r=<repositories>]... [artifacts...]

Parameters:
  [artifacts...]        One or more artifacts to resolve. Artifacts have the
                        format <group>:<artifact>[:<extension>
                        [:<classifier>]]:<version>

Options:
  -a, --appinfo=<appInfoFile>
                        App info file to use (default './app.yml')

Example:
  jpm install org.apache.httpcomponents:httpclient:4.5.14
  jpm install                  # Install dependencies from app.yml

copy (alias: c)

Copy artifacts to a directory without modifying app.yml.

Usage: jpm copy [-Lqsv] [-c=<cacheDir>] [-d=<directory>] [-r=<repositories>]...
                artifacts...

Parameters:
  artifacts...          One or more artifacts to resolve. Artifacts have the
                        format <group>:<artifact>[:<extension>
                        [:<classifier>]]:<version>

Options:
  -s, --sync            Makes sure the target directory will only contain
                        the mentioned artifacts and their dependencies,
                        possibly removing other files present in the
                        directory

Example:
  jpm copy org.apache.httpcomponents:httpclient:4.5.14

path (alias: p)

Print the classpath for the specified artifacts or app.yml dependencies.

Usage: jpm path [-Lv] [-a=<appInfoFile>] [-c=<cacheDir>] [-d=<directory>]
                [-r=<repositories>]... [artifacts...]

Parameters:
  [artifacts...]        One or more artifacts to resolve. Artifacts have the
                        format <group>:<artifact>[:<extension>
                        [:<classifier>]]:<version>

Options:
  -a, --appinfo=<appInfoFile>
                        App info file to use (default './app.yml')

Example:
  jpm path org.apache.httpcomponents:httpclient:4.5.14
  jpm path             # Print classpath from app.yml dependencies

do

Execute an action defined in app.yml.

Usage: jpm do [-lLqv] [-a=<appInfoFile>] [-c=<cacheDir>] [-d=<directory>]
              [-r=<repositories>]... [action...] [actionsAndArguments...]

Parameters:
  [action...]           Name of the action to execute as defined in app.yml
  [actionsAndArguments...]
                        Optional additional actions and/or arguments to be
                        passed to the action(s)

Options:
  -l, --list            List all available actions
  -a, --appinfo=<appInfoFile>
                        App info file to use (default './app.yml')

Example:
  jpm do --list                # List all actions
  jpm do build                 # Execute the build action
  jpm do test --arg verbose    # Pass 'verbose' arg to test action
  jpm do build -a --fresh test -a verbose  # Chain actions

clean / build / run / test

Convenient aliases for executing the corresponding action from app.yml.

Usage: jpm clean [args...]
Usage: jpm build [args...]
Usage: jpm run [args...]
Usage: jpm test [args...]

Parameters:
  [args...]             Optional arguments to pass to the action

Options:
  -a, --appinfo=<appInfoFile>
                        App info file to use (default './app.yml')

Example:
  jpm build
  jpm run --verbose debug
  jpm test

exec

Execute a shell command with platform-independent path handling.

Usage: jpm exec [-Lqv] [-a=<appInfoFile>] [-c=<cacheDir>] [-d=<directory>]
                [-r=<repositories>]... [command...]

Parameters:
  [command...]          The command to execute

Options:
  -a, --appinfo=<appInfoFile>
                        App info file to use (default './app.yml')

Supported tokens:
  {{deps}}              The classpath of all dependencies defined in app.yml
  {/}                   The OS' file path separator
  {:}                   The OS' class path separator
  {~}                   The user's home directory using the OS' class path format
  {;}                   The OS' command separator
  {./file/path}         A path using the OS' path format (must start with './'!)
  {./lib:./ext}         A class path using the OS' class path format
  @[ ... ]              Writes contents to a file and inserts @<path-to-file>

Example:
  jpm exec javac -cp {{deps}} -d out/classes --source-path src/main/java App.java
  jpm exec java -cp {{deps}} Main
  jpm exec @kotlinc -cp {{deps}} -d out/classes src/main/kotlin/App.kt

app.yml File Format

The app.yml file is used by jpm to track project dependencies, define custom repositories, and configure actions for your Java project. This file makes it easy to share your project with others - they can simply run jpm install to get all required dependencies.

File Structure

name: MyApp
description: A sample Java application
documentation: https://github.com/user/myapp
java: 11+
authors:
  - John Doe <john@example.com>
  - Jane Smith <jane@example.com>
contributors:
  - Bob Johnson <bob@example.com>

dependencies:
  com.github.lalyos:jfiglet: 0.0.9
  info.picocli:picocli: 4.7.7
  org.yaml:snakeyaml: '2.5'

repositories:
  myrepo: https://my.repo.com/maven2
  private: https://private.repo.com/releases

actions:
  setup: "echo Setting up the project..."
  build: "javac -cp {{deps}} -d {./target/classes} src{/}*.java"
  run: "java -cp {{deps}}{:}{./target/classes} Main"
  test: "java -cp {{deps}}{:}{./target/classes} org.junit.runner.JUnitCore TestSuite"
  clean: "rm -rf {./target}"

Fields

Metadata Fields (Optional)

  • name - The name of your application
  • description - A brief description of what the application does
  • documentation - URL to documentation or project homepage
  • java - Minimum Java version required (e.g., 11, 17)
  • authors - List of original authors with optional email addresses
  • contributors - List of contributors with optional email addresses

dependencies (Required for most projects)

Lists the Maven artifacts your project depends on. Each dependency is specified as groupId:artifactId: version.

dependencies:
  com.github.lalyos:jfiglet: 0.0.9
  info.picocli:picocli: 4.7.7
  org.yaml:snakeyaml: '2.5'

Note: Version numbers should be quoted if they could be interpreted as numbers (e.g., '2.5' instead of 2.5).

You can also use the list format:

dependencies:
  - com.github.lalyos:jfiglet:0.0.9
  - info.picocli:picocli:4.7.7

repositories (Optional)

Custom Maven repositories to search for artifacts. Useful for private repositories or alternative artifact sources.

repositories:
  myrepo: https://my.repo.com/maven2
  private: https://private.repo.com/releases

For repositories requiring authentication, set environment variables:

  • JPM_REPO_<name>_USER - Username for the repository
  • JPM_REPO_<name>_PASSWORD - Password for the repository

Example: For a repository named private, use JPM_REPO_PRIVATE_USER and JPM_REPO_PRIVATE_PASSWORD.

actions (Optional)

Custom commands that can be executed with jpm do <action-name>. Actions support variable substitution for cross-platform compatibility.

actions:
  build: "javac -cp {{deps}} -d {./target/classes} src{/}*.java"
  run: "java -cp {{deps}}{:}{./target/classes} Main"
  test: "java -cp {{deps}} org.junit.runner.JUnitCore TestSuite"
  clean: "rm -rf {./target}"

See the Actions section above for details on variable substitution and using the jpm do, jpm build, jpm run, jpm test, and jpm clean commands.

Minimal Example

A minimal app.yml file only needs dependencies:

dependencies:
  com.github.lalyos:jfiglet: 0.0.9

Complete Example

Here's a complete example showing all available fields:

name: HelloWorld
description: A simple Java application that prints ASCII art
documentation: https://github.com/user/hello-world
java: 11+
authors:
  - Alice Developer <alice@example.com>
contributors:
  - Bob Contributor <bob@example.com>

dependencies:
  com.github.lalyos:jfiglet: 0.0.9
  info.picocli:picocli: 4.7.7

repositories:
  jitpack: https://jitpack.io

actions:
  build: "javac -cp {{deps}} *.java"
  run: "java -cp {{deps}} HelloWorld"
  clean: "rm -f *.class"

Configuration File

jpm supports user-level configuration files to set default values for common command-line options. This allows you to configure preferences once instead of passing the same options repeatedly.

Configuration File Locations

Configuration files are searched in the following order:

  1. Explicit path - Specified via --config option or JPM_CONFIG environment variable
  2. ~/.config/jpm/config.yml - XDG Base Directory standard location (primary)
  3. ~/.jpmcfg.yml - Fallback location (home directory)

The first file found is used. If no configuration file exists, jpm uses built-in defaults.

Configuration File Format

config:
  cache: ~/my-jpm-cache
  directory: libs
  no-links: false
  repositories:
    myrepo: https://my.repo.com/maven2
    private: https://private.repo.com/releases

Configuration Options

  • cache - Directory for caching downloaded artifacts (equivalent to --cache option)
  • directory - Default directory to copy artifacts to (equivalent to --directory option)
  • no-links - Whether to copy files instead of creating symlinks (equivalent to --no-links option)
  • repositories - Map of repository names to URLs (merged with --repo options)

Path Expansion

Paths in the configuration file support home directory expansion:

config:
  cache: ~/jpm-cache          # Expands to /home/user/jpm-cache
  directory: ~/.local/lib/jpm # Expands to /home/user/.local/lib/jpm

Development

To build the project simply run:

./mvnw spotless:apply clean install

Of course, once you've got jpm installed you can do:

jpm do clean build
jpm test
jpm run

About

A simple command line tool, taking inspiration from Node's npm, to manage Maven dependencies for Java projects that are not using build systems like Maven or Gradle.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages