GitHub

Original file line numberDiff line numberDiff line change

@@ -1,6 +1,6 @@

11

[package]

22

name = "jav"

3-

version = "2026.623.8"

3+

version = "2026.624.1"

44

edition = "2021"

55

description = "A modern CLI for Java projects"

66

license = "MIT"

Original file line numberDiff line numberDiff line change

@@ -34,6 +34,8 @@ Build and run support `debug` and `release` configurations. For generated projec

3434
3535

Generated runnable projects include a `jav.toml` file for run defaults such as main class and Maven/Gradle task. Edit it when a project needs a custom run shape.

3636
37+

Projects also generate a `flake.nix` by default for a pinned Java/build-tool environment. Use `--no-flake` if you do not want Nix files.

38+
3739

## Templates

3840
3941

Installed templates:

@@ -62,6 +64,8 @@ Supported build tools are `maven` and `gradle`. Pass `--build-tool` to override

6264
6365

Template aliases are supported too, for example `webapi` for `springweb` and `classlib` for `library`.

6466
67+

Generated dependency versions are pinned in Maven/Gradle files. For fully reproducible project builds, commit `flake.lock` and enable Maven/Gradle dependency locking as needed for the project.

68+
6569

## Install

6670
6771

Run without installing through Nix:

Original file line numberDiff line numberDiff line change

@@ -111,6 +111,9 @@ pub struct NewArgs {

111111

/// Build tool to generate: maven or gradle. Defaults depend on the template.

112112

#[arg(long)]

113113

pub build_tool: Option<String>,

114+

/// Do not generate a Nix flake for the project.

115+

#[arg(long)]

116+

pub no_flake: bool,

114117

/// Java language version.

115118

#[arg(long, default_value = "21")]

116119

pub java_version: String,

Original file line numberDiff line numberDiff line change

@@ -65,6 +65,7 @@ pub fn run(args: NewArgs) -> Result<()> {

6565

spring_boot_version: args.spring_boot_version,

6666

spring_features,

6767

main_class,

68+

include_flake: !args.no_flake,

6869

};

6970
7071

render::render(&template_manifest.id, &destination, context).with_context(|| {

Original file line numberDiff line numberDiff line change

@@ -43,6 +43,7 @@ pub struct TemplateContext {

4343

pub spring_boot_version: String,

4444

pub spring_features: Vec<String>,

4545

pub main_class: String,

46+

pub include_flake: bool,

4647

}

4748
4849

pub const TEMPLATE_IDS: &[&str] = &[

@@ -181,7 +182,7 @@ pub fn files(

181182

manifest: &TemplateManifest,

182183

context: &TemplateContext,

183184

) -> Option<Vec<(&'static str, &'static str)>> {

184-

match manifest.renderer.as_str() {

185+

let mut files = match manifest.renderer.as_str() {

185186

"plain-app" => Some(match context.build_tool.as_str() {

186187

"gradle" => vec![

187188

(".gitignore", include_str!("../../templates/common/gitignore")),

@@ -304,7 +305,16 @@ pub fn files(

304305

Some(files)

305306

}

306307

_ => None,

308+

}?;

309+
310+

if context.include_flake {

311+

files.push((

312+

"flake.nix",

313+

include_str!("../../templates/common/flake.nix"),

314+

));

307315

}

316+
317+

Some(files)

308318

}

309319
310320

fn plain_main_file(template: &str) -> &'static str {

Original file line numberDiff line numberDiff line change

@@ -56,6 +56,17 @@ fn tera_context(context: &TemplateContext) -> TeraContext {

5656

tera_context.insert("package_path", &context.package_path);

5757

tera_context.insert("java_version", &context.java_version);

5858

tera_context.insert("build_tool", &context.build_tool);

59+

tera_context.insert("is_maven", &(context.build_tool == "maven"));

60+

tera_context.insert("is_gradle", &(context.build_tool == "gradle"));

61+

tera_context.insert(

62+

"is_spring",

63+

&context.spring_features.iter().any(|feature| {

64+

matches!(

65+

feature.as_str(),

66+

"web" | "actuator" | "data-jpa" | "security" | "postgresql" | "batch"

67+

)

68+

}),

69+

);

5970

tera_context.insert("spring_boot_version", &context.spring_boot_version);

6071

tera_context.insert("main_class", &context.main_class);

6172

tera_context.insert(

Original file line numberDiff line numberDiff line change

@@ -10,5 +10,16 @@ jav test

1010

jav run

1111

```

1212
13+

With Nix:

14+
15+

```bash

16+

nix develop

17+

nix run .#build

18+

nix run .#test

19+

nix run .#run

20+

```

21+
22+

Commit `flake.lock` after the first Nix command if you want the development environment pinned exactly.

23+
1324

Build tool: `{{ build_tool }}`

1425

Java version: `{{ java_version }}`

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,64 @@

1+

{

2+

description = "{{ project_name }}";

3+
4+

inputs = {

5+

nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

6+

};

7+
8+

outputs = { self, nixpkgs, ... }:

9+

let

10+

systems = [

11+

"x86_64-linux"

12+

"aarch64-linux"

13+

"x86_64-darwin"

14+

"aarch64-darwin"

15+

];

16+
17+

forAllSystems = nixpkgs.lib.genAttrs systems;

18+
19+

projectFor = system:

20+

let

21+

pkgs = import nixpkgs { inherit system; };

22+

buildTool = {% if is_maven %}pkgs.maven{% else %}pkgs.gradle{% endif %};

23+

in

24+

{

25+

devShells.default = pkgs.mkShell {

26+

packages = [

27+

buildTool

28+

pkgs.jdk{{ java_version }}

29+

];

30+
31+

JAVA_HOME = pkgs.jdk{{ java_version }};

32+

};

33+
34+

apps = {

35+

build = {

36+

type = "app";

37+

program = "${pkgs.writeShellScript "{{ project_name }}-build" ''

38+

{% if is_maven %}exec ${buildTool}/bin/mvn package "$@"{% else %}exec ${buildTool}/bin/gradle build "$@"{% endif %}

39+

''}";

40+

};

41+
42+

test = {

43+

type = "app";

44+

program = "${pkgs.writeShellScript "{{ project_name }}-test" ''

45+

{% if is_maven %}exec ${buildTool}/bin/mvn test "$@"{% else %}exec ${buildTool}/bin/gradle test "$@"{% endif %}

46+

''}";

47+

};

48+
49+

run = {

50+

type = "app";

51+

program = "${pkgs.writeShellScript "{{ project_name }}-run" ''

52+

{% if is_maven and is_spring %}exec ${buildTool}/bin/mvn spring-boot:run "$@"{% elif is_maven %}exec ${buildTool}/bin/mvn compile exec:java "$@"{% elif is_spring %}exec ${buildTool}/bin/gradle bootRun "$@"{% else %}exec ${buildTool}/bin/gradle run "$@"{% endif %}

53+

''}";

54+

};

55+
56+

default = self.apps.${system}.run;

57+

};

58+

};

59+

in

60+

{

61+

devShells = forAllSystems (system: (projectFor system).devShells);

62+

apps = forAllSystems (system: (projectFor system).apps);

63+

};

64+

}

Original file line numberDiff line numberDiff line change

@@ -52,6 +52,7 @@ fn new_console_creates_maven_project() {

5252

project

5353

.child(".gitignore")

5454

.assert(predicate::path::exists());

55+

project.child("flake.nix").assert(predicate::path::exists());

5556

project.child("jav.toml").assert(predicate::path::exists());

5657

project

5758

.child("src/main/java/dev/example/demo/Main.java")

@@ -61,6 +62,32 @@ fn new_console_creates_maven_project() {

6162

.assert(predicate::path::exists());

6263

}

6364
65+

#[test]

66+

fn new_can_skip_flake_generation() {

67+

let temp = assert_fs::TempDir::new().unwrap();

68+

let project = temp.child("NoFlake");

69+
70+

let mut command = Command::cargo_bin("jav").unwrap();

71+

command

72+

.current_dir(temp.path())

73+

.args([

74+

"new",

75+

"console",

76+

"--name",

77+

"NoFlake",

78+

"--package",

79+

"dev.example.noflake",

80+

"--no-flake",

81+

])

82+

.assert()

83+

.success();

84+
85+

project

86+

.child("flake.nix")

87+

.assert(predicate::path::missing());

88+

project.child("pom.xml").assert(predicate::path::exists());

89+

}

90+
6491

#[test]

6592

fn new_without_template_prints_catalog() {

6693

let mut command = Command::cargo_bin("jav").unwrap();

Read the original on github.com ↗