Skip to content

Conversation

@avelino
Copy link
Member

@avelino avelino commented Oct 10, 2025

  • Add GitHub Actions workflow build-native-image.yml for GraalVM native-image binary builds on macOS and Ubuntu
  • Update .gitignore for native-image build artifacts and outputs
  • Provide instructions for building and testing native images in documentation (see README and copilot-instructions)
  • Enable CI artifact upload for platform-specific native binaries

Motivation:
Enables efficient cross-platform deployments and easier distribution of ChronDB as a native binary, improving startup time and resource usage, especially for cloud and container environments.

Related:

  • Docs: AGENT.md, README.md, .github/copilot-instructions.md
  • Native image support: dev/chrondb/native_image.clj, build script, updated ignore rules

fixed: #12

Summary by CodeRabbit

  • New Features

    • Added GraalVM native image build support for creating standalone OS-specific binaries.
    • Added Docker-based native build and runtime image for distribution.
  • Documentation

    • Added native-image build and usage guidance to operations and changelog.
    • Added AGENT.md as a developer/contributor guide.
  • Chores

    • Added CI workflows to build/publish native binaries and Docker images.
    • Introduced build tooling and updated deps; expanded .gitignore for native artifacts.

- Add GitHub Actions workflow `build-native-image.yml` for GraalVM native-image binary builds on macOS and Ubuntu
- Update `.gitignore` for native-image build artifacts and outputs
- Provide instructions for building and testing native images in documentation (see README and copilot-instructions)
- Enable CI artifact upload for platform-specific native binaries

Motivation:
Enables efficient cross-platform deployments and easier distribution of ChronDB as a native binary,
improving startup time and resource usage, especially for cloud and container environments.

Related:
- Docs: AGENT.md, README.md, .github/copilot-instructions.md
- Native image support: `dev/chrondb/native_image.clj`, build script, updated ignore rules

fixed: #12

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds GraalVM native-image support and related tooling: CI workflow, Dockerfile, build scripts and native-image config generator; updates deps, gitignore, docs (AGENT.md, operations), changelog, and minor type hints/AOT and JGit type annotations across source files.

Changes

Cohort / File(s) Summary of Changes
Documentation & Guidance
AGENT.md, docs/operations.md, CHANGELOG.md, .github/copilot-instructions.md
New AGENT guidance and docs for GraalVM native-image build; documents workflow and updates changelog and copilot instructions.
CI / Workflow
.github/workflows/build-native-image.yml, .github/workflows/publish-docker.yml
Adds GitHub Actions workflows: one to build GraalVM native images across OS runners and upload artifacts; another to build/push Docker images to registry.
Build Tooling & Scripts
deps.edn, dev/chrondb/build.clj, dev/chrondb/native_image.clj
Adds graal-build-time dependency and build alias changes; introduces chrondb.build (build orchestration) and chrondb.native-image (prepare native-image args, reflect/resource configs, filter, trace configs; public prepare-files).
Docker / Runtime packaging
Dockerfile
Adds multi-stage Dockerfile to build GraalVM native image and produce a minimal runtime image with ports and non-root user.
Git ignore
.gitignore
Adds patterns to ignore native-image artifacts, META-INF, build artifacts, and local env files.
Core / AOT
src/chrondb/core.clj
Adds :gen-class to the namespace for AOT/Java class generation.
Server API (minor edits)
src/chrondb/api/server.clj, src/chrondb/api/redis/core.clj, src/chrondb/api/sql/connection/server.clj
Minor syntax and type-hint edits: Jetty call parentheses correction and ^ServerSocket hints added; no behavioral changes intended.
Storage / JGit typing
src/chrondb/storage/git/commit.clj
Introduces explicit JGit type hints (^Git, ^Repository, StoredConfig, ObjectInserter), updates several function signatures and internal bindings to use typed JGit objects.
Misc
AGENT.md (created empty in one change context)
Adds AGENT.md (guidance/documentation file; some diffs show detailed content, others show empty placeholder).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer / CI
  participant Build as chrondb.build
  participant NI as chrondb.native-image
  participant GI as GraalVM_native-image
  participant Store as Artifact Upload

  Dev->>Build: invoke -m chrondb.build [--uberjar]
  Build->>Build: clean, write POM, copy & compile sources
  Build->>Build: package jar / uberjar
  Build->>NI: prepare-files (generate native-image args & configs)
  NI-->>Build: return args & config paths
  Build->>GI: native-image @native-image-args (OS-specific)
  GI-->>Build: produce binary (chrondb_<os>)
  Build->>Store: upload artifact / Docker registry
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

documentation

Poem

I nibble at builds with whiskers bright,
from jar to native in the moonlight. 🐇
I pack reflect bits and resources too,
a single binary carrot just for you.
Hop—chrondb runs, light and new.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request includes several changes unrelated to implementing GraalVM native image support, such as extensive updates to AGENT.md, type hint adjustments in unrelated modules, Docker publishing workflows, and generic Codox ignore pattern changes that fall outside the scope of issue #12. Please extract or remove unrelated documentation enhancements, type hint refactors, Docker publish workflow, and generic ignore pattern changes into separate pull requests to maintain focused scope on GraalVM native image support.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately describes the primary change of adding a cross-platform GraalVM native-image GitHub Actions workflow and clearly conveys the main intent of the pull request in a concise, single sentence.
Linked Issues Check ✅ Passed The pull request delivers the coding-related elements from issue #12 by introducing native-image build configuration files, updating the build process with new scripts and workflows, and documenting the GraalVM build steps, and it omits only manual test instructions which are outside code requirements.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch avelino/native-image-builder

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive GraalVM native-image build support to ChronDB, enabling cross-platform native binary distribution for improved startup performance and resource usage.

Key changes:

  • Added GitHub Actions workflow for automated cross-platform native binary builds on macOS and Ubuntu
  • Implemented native-image configuration and build tooling with proper reflection and resource settings
  • Added type hints throughout the codebase to optimize GraalVM compilation

Reviewed Changes

Copilot reviewed 12 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/build-native-image.yml GitHub Actions workflow for building native binaries on macOS/Ubuntu
dev/chrondb/native_image.clj Native-image configuration generator with GraalVM-specific settings
dev/chrondb/build.clj Build script for JAR creation and native-image preparation
src/chrondb/core.clj Added :gen-class for native-image main class support
src/chrondb/storage/git/commit.clj Added type hints for JGit classes to improve native compilation
src/chrondb/api/sql/connection/server.clj Added type hint for ServerSocket
src/chrondb/api/redis/core.clj Added type hints for ServerSocket instances
docs/operations.md Documentation for building and using native images
AGENT.md Comprehensive project guidelines moved from .github/copilot-instructions.md
CHANGELOG.md Updated to reflect native binary support addition
.github/copilot-instructions.md Replaced with reference to AGENT.md
.cursorrules Added reference to AGENT.md
Comments suppressed due to low confidence (1)

dev/chrondb/native_image.clj:1

  • Environment variable name should be 'CHRONDB_VERSION' instead of 'CHROND_VERSION' to match the project name.
(ns chrondb.native-image

Comment on lines 222 to 226
(doseq [[fname contents] [["reflect-config.json" "[]"]
["resource-config.json" (json/write-str {:resources {:includes []}})]]]
(let [file (io/file native-config-dir fname)]
(when-not (.exists file)
(spit file contents))))))
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback configuration creation is redundant since the same files are already created with proper content earlier in the function (lines 220-221). The fallback logic should be removed to avoid confusion.

Suggested change
(doseq [[fname contents] [["reflect-config.json" "[]"]
["resource-config.json" (json/write-str {:resources {:includes []}})]]]
(let [file (io/file native-config-dir fname)]
(when-not (.exists file)
(spit file contents))))))
)))

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/chrondb/storage/git/commit.clj (1)

70-81: Return a real Ref for bare repos; avoid reified stub.

Reifying Ref with only getName risks downstream breakage. Create/update the ref and return the actual repository ref.

-        (if ref
-          (reify org.eclipse.jgit.lib.Ref
-            (getName [_] branch-name))
-          ;; If the branch doesn't exist, create it
-          (let [ref-update (.updateRef repository (str "refs/heads/" branch-name))
-                head-id (.resolve repository Constants/HEAD)]
-            (.setNewObjectId ref-update (or head-id (ObjectId/zeroId)))
-            (.update ref-update)
-            (reify org.eclipse.jgit.lib.Ref
-              (getName [_] branch-name)))))
+        (if ref
+          ref
+          (let [ref-name (str "refs/heads/" branch-name)
+                ref-update (.updateRef repository ref-name)
+                head-id (.resolve repository Constants/HEAD)]
+            (.setNewObjectId ref-update (or head-id (ObjectId/zeroId)))
+            (.update ref-update)
+            (.exactRef repository ref-name))))
deps.edn (1)

10-12: Align Lucene versions & enable uberjar in :build alias.

  • Bump lucene-analyzers-common to 9.8.0 (match other modules).
  • Add "--uberjar" to :build alias :main-opts so chrondb.build produces an uberjar for native-image.

Apply these diffs:

--- deps.edn
@@ -10,7 +10,7 @@
              org.apache.lucene/lucene-core             {:mvn/version "9.8.0"}
              org.apache.lucene/lucene-queryparser      {:mvn/version "9.8.0"}
-             org.apache.lucene/lucene-analyzers-common {:mvn/version "8.11.2"}
+             org.apache.lucene/lucene-analyzers-common {:mvn/version "9.8.0"}
@@ -82,7 +82,8 @@
            :build {:deps {io.github.clojure/tools.build {:git/tag "v0.9.4" :git/sha "76b78fe"}}
-                   :extra-paths ["dev"]
-                   :main-opts ["-m" "chrondb.build"]}
+                   :extra-paths ["dev"]
+                   :main-opts ["-m" "chrondb.build" "--uberjar"]}
🧹 Nitpick comments (5)
.github/workflows/build-native-image.yml (1)

24-30: Upgrade GraalVM to v25
The workflow currently pins GraalVM 22.3.2 (Sept 2022). As of Oct 2025, GraalVM 25 (Sept 16 2025) is available with key native-image improvements:

  • WP-SCCP enabled by default for smaller, more accurate images
  • Refined O2/O3 control-split profile models
  • Default SBOM embedding and experimental symbol obfuscation
  • New -H:Preserve option and enhanced Vector API support
    Evaluate upgrading to v25 in .github/workflows/build-native-image.yml and test compatibility. If you must stay on 22.3.2, please document the rationale.
AGENT.md (1)

96-101: Doc: include --uberjar for native-image build flow.

Without an uberjar, native-image will miss dependencies and likely fail. Update the command.

- - Use `clojure -M:build` to gerar o artefato `target/chrondb.jar` e preparar arquivos auxiliares para o GraalVM
+ - Use `clojure -M:build --uberjar` para gerar o artefato `target/chrondb.jar` e preparar arquivos auxiliares para o GraalVM
- - Para gerar o binário nativo localmente após rodar o build, execute `native-image @target/native-image-args -jar target/chrondb.jar -o target/chrondb_local`
+ - Para gerar o binário nativo localmente após rodar o build, execute `native-image @target/native-image-args -jar target/chrondb.jar -o target/chrondb_local`
dev/chrondb/native_image.clj (2)

140-145: Gate -O0 and -H:-CheckToolchain with env vars (CI vs local).

-O0 speeds builds but hurts runtime; -H:-CheckToolchain can hide misconfigs. Make them configurable.

-                   "-H:-CheckToolchain"
-                   "-O0" ;; performance
+                   (when (= "0" (System/getenv "NATIVE_CHECK_TOOLCHAIN")) "-H:-CheckToolchain")
+                   (or (System/getenv "NATIVE_O") "-O0") ;; default O0; set NATIVE_O to e.g. -O3 for releases

Note: filter out nils before writing args (e.g., via remove nil?). For example:

(let [tail-args (->> [... (when ...) (or ...)] (remove nil?))]
  ...)

175-221: Optional: drop test-only class from reflect-config to reduce noise.

org.apache.lucene.internal.tests.TestSecrets is a test-only class; referencing it can produce warnings during native-image.

-                          {:name "org.apache.lucene.internal.tests.TestSecrets"
-                           :allDeclaredConstructors true
-                           :allDeclaredMethods true}
dev/chrondb/build.clj (1)

93-104: Warn when building thin jar (to avoid native-image classpath issues).

If users forget --uberjar, native-image likely fails. Emit a clear warning.

   (let [uberjar? (boolean (some #(= % "--uberjar") args))
         basis (build-basis)
         options (build-options basis)]
+    (when-not uberjar?
+      (println "[WARN] Building a thin JAR. Native-image requires dependencies on the classpath or an uberjar. Consider passing --uberjar."))
     (clean-target!)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4283d84 and ebbb8a6.

📒 Files selected for processing (16)
  • .cursorrules (1 hunks)
  • .github/copilot-instructions.md (0 hunks)
  • .github/copilot-instructions.md (1 hunks)
  • .github/workflows/build-native-image.yml (1 hunks)
  • .gitignore (1 hunks)
  • AGENT.md (1 hunks)
  • CHANGELOG.md (1 hunks)
  • deps.edn (2 hunks)
  • dev/chrondb/build.clj (1 hunks)
  • dev/chrondb/native_image.clj (1 hunks)
  • docs/operations.md (1 hunks)
  • src/chrondb/api/redis/core.clj (2 hunks)
  • src/chrondb/api/server.clj (1 hunks)
  • src/chrondb/api/sql/connection/server.clj (1 hunks)
  • src/chrondb/core.clj (1 hunks)
  • src/chrondb/storage/git/commit.clj (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run SQL Protocol Benchmark
  • GitHub Check: test
🔇 Additional comments (7)
src/chrondb/api/server.clj (1)

62-62: LGTM: Syntax correction applied.

The extra closing parenthesis has been correctly removed.

src/chrondb/api/sql/connection/server.clj (1)

36-36: LGTM: Type hint improves native-image compilation.

The ^ServerSocket type hint eliminates reflection and supports GraalVM native-image compilation.

CHANGELOG.md (1)

9-9: LGTM: Changelog entry is accurate.

The changelog appropriately documents the new GraalVM native binary support.

.github/workflows/build-native-image.yml (1)

32-35: Ignore missing -jar warning: args file includes the jar path. The build script (dev/chrondb/native_image.clj:122–128) injects -jar target/chrondb.jar into native-image-args, so the workflow command is correct as-is.

Likely an incorrect or invalid review comment.

src/chrondb/core.clj (1)

17-17: LGTM! Essential for GraalVM native-image support.

The :gen-class directive enables AOT compilation and Java class generation, which is required for creating a native-image binary with a proper entry point. This aligns perfectly with the PR objectives to enable standalone binary distribution.

src/chrondb/api/redis/core.clj (2)

535-535: LGTM! Type hint eliminates reflection.

The ^ServerSocket type hint on the function parameter eliminates reflection on subsequent method calls (.isClosed, .accept), which is essential for GraalVM native-image compilation.


555-555: LGTM! Type hint eliminates reflection.

The ^ServerSocket type hint on the local binding eliminates reflection on the ServerSocket. constructor and ensures the type is known for the subsequent accept-connections call.

Comment on lines 16 to 19
- uses: DeLaGuardo/setup-clojure@13.4
with:
cli: 1.12.0.1530
github-token: ${{ secrets.GITHUB_TOKEN }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify GitHub Actions versions are current.

Consider checking if you're using the latest versions of the GitHub Actions:

  • DeLaGuardo/setup-clojure@13.4
  • Clojure CLI 1.12.0.1530

Search the web to confirm these are the latest stable versions:


🌐 Web query:

What is the latest version of DeLaGuardo/setup-clojure GitHub Action and Clojure CLI as of October 2025?

💡 Result:

  • DeLaGuardo/setup-clojure GitHub Action: v12.5. [1]

  • Clojure CLI (stable): 1.12.3 (tools release 1.12.3.1577). [2][3]

Sources:
[1] GitHub Marketplace — Setup Clojure (DeLaGuardo/setup-clojure). (github.com)
[2] Clojure downloads — Stable Release: 1.12.3 (Sep 25, 2025). (clojure.org)
[3] Clojure Tools releases (1.12.3.1577). (clojure.org)

Citations:


Update GitHub Action and Clojure CLI to latest stable versions.

  • Replace DeLaGuardo/setup-clojure@13.4 with @v12.5
  • Set Clojure CLI to 1.12.3.1577
🤖 Prompt for AI Agents
.github/workflows/build-native-image.yml lines 16-19: update the GitHub Action
and Clojure CLI version values; replace the action reference
DeLaGuardo/setup-clojure@13.4 with DeLaGuardo/setup-clojure@v12.5 and change the
cli version from 1.12.0.1530 to 1.12.3.1577, leaving the github-token usage
intact.

.gitignore Outdated
Comment on lines 21 to 40
report/
META-INF/*-native-image-args
.native-image/

# GraalVM native-image artifacts
target/native/
target/chrondb_*
chrondb_local

# OS
.DS_Store
Thumbs.db

# doc codox
docs/api/
META-INF/
.envrc
./chrondb_local
./chrondb_macos
./chrondb_linux
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Clean up duplicate and redundant ignore patterns.

Several issues with the new ignore patterns:

  1. Duplicate entries:

    • chrondb_local appears at both line 28 and line 38 (with ./ prefix)
    • META-INF/ entries at lines 22 and 36
  2. Redundant ./ prefix:

    • Lines 38-40 use explicit ./ prefix which is unnecessary in .gitignore (paths are relative to repository root by default)

Apply this diff to consolidate the patterns:

 # Build
 target/
 data/
 test-*
 benchmark_results_*.txt
 report/
-META-INF/*-native-image-args
 .native-image/
 
 # GraalVM native-image artifacts
 target/native/
 target/chrondb_*
 chrondb_local
+chrondb_macos
+chrondb_linux
 
 # OS
 .DS_Store
 Thumbs.db
 
 # doc codox
 docs/api/
 META-INF/
 .envrc
-./chrondb_local
-./chrondb_macos
-./chrondb_linux
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
report/
META-INF/*-native-image-args
.native-image/
# GraalVM native-image artifacts
target/native/
target/chrondb_*
chrondb_local
# OS
.DS_Store
Thumbs.db
# doc codox
docs/api/
META-INF/
.envrc
./chrondb_local
./chrondb_macos
./chrondb_linux
# Build
target/
data/
test-*
benchmark_results_*.txt
report/
.native-image/
# GraalVM native-image artifacts
target/native/
target/chrondb_*
chrondb_local
chrondb_macos
chrondb_linux
# OS
.DS_Store
Thumbs.db
# doc codox
docs/api/
META-INF/
.envrc
🤖 Prompt for AI Agents
In .gitignore around lines 21 to 40, there are duplicate and redundant patterns:
remove the duplicated META-INF/ and duplicate chrondb_local entry (the one with
./ prefix), and drop the unnecessary ./ prefixes on chrondb_macos and
chrondb_linux; consolidate entries so each ignore pattern appears once (e.g.,
keep META-INF/ once, keep chrondb_local without ./, and list chrondb_macos and
chrondb_linux without ./), preserving the rest of the ignore lines and ordering
as desired.

Comment on lines +83 to 85
:extra-paths ["dev"]
:main-opts ["-m" "chrondb.build"]}
:dev {:extra-paths ["dev"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Build alias should produce an uberjar (native-image needs deps on classpath).

native-image-args currently use only target/classes + target/chrondb.jar. If the jar is thin (no deps), local native-image will fail. CI likely passes --uberjar, but AGENT.md instructs just -M:build.

Recommend making :build default to uberjar to reduce footguns.

Apply this diff:

-                     :main-opts ["-m" "chrondb.build"]}
+                     :main-opts ["-m" "chrondb.build" "--uberjar"]}

If you prefer not to force uberjar by default, at least update docs in AGENT.md to instruct using --uberjar.


🏁 Script executed:

#!/bin/bash
rg -nP '^\s*:aliases\s+\{' -C5 deps.edn
rg -nP '^\s*:build\s+\{' -C3 deps.edn

Length of output: 1361


🏁 Script executed:

#!/bin/bash
# Locate and inspect build script
build_file=$(rg -l 'ns chrondb.build' -g '*.clj')
echo "Build script: $build_file"
rg -n 'defn -main' -C5 "$build_file"
rg -n ':uberjar' -C3 "$build_file"

# Locate and inspect AGENT.md
agent_file=$(find . -iname AGENT.md)
echo "Agent doc: $agent_file"
rg -n 'M:build' -C3 "$agent_file"
rg -n 'uberjar' -C3 "$agent_file"

Length of output: 950


Force --uberjar in the :build alias
Add --uberjar to :main-opts in deps.edn so the default build produces an uberjar for native-image:

File: deps.edn
@@ -83,1 +83,1
-                     :main-opts ["-m" "chrondb.build"]}
+                     :main-opts ["-m" "chrondb.build" "--uberjar"]}

Alternatively, if you prefer not to change the alias, update AGENT.md to instruct using clojure -M:build -- --uberjar.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
:extra-paths ["dev"]
:main-opts ["-m" "chrondb.build"]}
:dev {:extra-paths ["dev"]
:extra-paths ["dev"]
:main-opts ["-m" "chrondb.build" "--uberjar"]}
:dev {:extra-paths ["dev"]
🤖 Prompt for AI Agents
In deps.edn around lines 83 to 85, the :build alias currently sets :main-opts
["-m" "chrondb.build"] but does not force building an uberjar; update the
:main-opts to include "--uberjar" (e.g. ["-m" "chrondb.build" "--uberjar"]) so
the default build produces an uberjar for native-image; alternatively, if you
prefer not to change the alias, update AGENT.md to instruct users to run clojure
-M:build -- --uberjar instead.

Comment on lines 121 to 130
path-sep (System/getProperty "path.separator")
classpath (string/join path-sep ["target/classes" "target/chrondb.jar"])
base-args ["-Dorg.slf4j.simpleLogger.defaultLogLevel=info"
"-Dorg.slf4j.simpleLogger.log.org.eclipse.jetty.server=warn"
"--features=clj_easy.graal_build_time.InitClojureClasses"
"-H:+UnlockExperimentalVMOptions"]
startup-args ["-cp" classpath
"-jar" "target/chrondb.jar"
"-H:Class=chrondb.core"]
tail-args ["--enable-all-security-services"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t mix -cp, -jar, and -H:Class; prefer a single mode.

Passing both -cp and -jar with -H:Class is redundant and can be confusing. If you’re building an uberjar, -jar is sufficient; drop -cp and -H:Class.

-        path-sep (System/getProperty "path.separator")
-        classpath (string/join path-sep ["target/classes" "target/chrondb.jar"])
+        ;; For uberjar builds, classpath is not needed
+        path-sep (System/getProperty "path.separator")
+        classpath (string/join path-sep ["target/classes" "target/chrondb.jar"])
         base-args ["-Dorg.slf4j.simpleLogger.defaultLogLevel=info"
                    "-Dorg.slf4j.simpleLogger.log.org.eclipse.jetty.server=warn"
                    "--features=clj_easy.graal_build_time.InitClojureClasses"
                    "-H:+UnlockExperimentalVMOptions"]
-        startup-args ["-cp" classpath
-                      "-jar" "target/chrondb.jar"
-                      "-H:Class=chrondb.core"]
+        startup-args ["-jar" "target/chrondb.jar"]

If you need a thin-jar path, create two branches: one with ["-cp" computed-classpath "chrondb.core"] and one with ["-jar" ...], selected based on an env var or presence of an uberjar.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
path-sep (System/getProperty "path.separator")
classpath (string/join path-sep ["target/classes" "target/chrondb.jar"])
base-args ["-Dorg.slf4j.simpleLogger.defaultLogLevel=info"
"-Dorg.slf4j.simpleLogger.log.org.eclipse.jetty.server=warn"
"--features=clj_easy.graal_build_time.InitClojureClasses"
"-H:+UnlockExperimentalVMOptions"]
startup-args ["-cp" classpath
"-jar" "target/chrondb.jar"
"-H:Class=chrondb.core"]
tail-args ["--enable-all-security-services"
;; For uberjar builds, classpath is not needed
path-sep (System/getProperty "path.separator")
classpath (string/join path-sep ["target/classes" "target/chrondb.jar"])
base-args ["-Dorg.slf4j.simpleLogger.defaultLogLevel=info"
"-Dorg.slf4j.simpleLogger.log.org.eclipse.jetty.server=warn"
"--features=clj_easy.graal_build_time.InitClojureClasses"
"-H:+UnlockExperimentalVMOptions"]
startup-args ["-jar" "target/chrondb.jar"]
tail-args ["--enable-all-security-services"
🤖 Prompt for AI Agents
In dev/chrondb/native_image.clj around lines 121 to 130, the command-line args
mix "-cp", "-jar", and "-H:Class" which is redundant/conflicting; choose a
single invocation mode. Remove "-cp" and "-H:Class" when using the uberjar
"-jar" form (keep startup-args as ["-jar" "target/chrondb.jar"]), or
alternatively provide a second branch that uses the thin-jar/classpath form
(startup-args ["-cp" computed-classpath "chrondb.core"]) and select between them
via an env var or by detecting the presence of the uberjar; update code to pick
one branch and eliminate the conflicting flags.

Comment on lines +141 to +150
### GraalVM Native Image

ChronDB pode ser distribuído como binário compilado via GraalVM:

1. Instale GraalVM com o componente `native-image`
2. Rode `clojure -M:build -- --uberjar` para gerar `target/chrondb.jar` e arquivos auxiliares
3. Execute `native-image @target/native-image-args -jar target/chrondb.jar -o target/chrondb_local`
4. Teste o binário local: `./target/chrondb_local`

O workflow `build-native-image.yml` (GitHub Actions) executa o mesmo processo e publica os artefatos por sistema operacional.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Fix language inconsistency and verify command accuracy.

Two issues with this section:

  1. Language inconsistency: Line 143 and subsequent text are in Portuguese while the rest of the document is in English. Please translate to English for consistency.

  2. Command discrepancy with workflow: Line 147's native-image command differs from the workflow at .github/workflows/build-native-image.yml line 35:

    • Docs: native-image @target/native-image-args -jar target/chrondb.jar -o target/chrondb_local
    • Workflow: native-image @native-image-args -H:Name=chrondb_${{ runner.os }} (executed from target/ directory)

The workflow doesn't explicitly specify -jar or the jar path. Verify that the args file contains these parameters, or update the docs to match the workflow command.

Run the following script to check if native-image-args contains jar specifications:


🏁 Script executed:

#!/bin/bash
# Check if native-image-args file will be generated with jar path
rg -n "native-image-args|chrondb.jar" dev/chrondb/

Length of output: 342


🏁 Script executed:

#!/bin/bash
# Inspect the flags written to native-image-args
sed -n '140,220p' dev/chrondb/native_image.clj

Length of output: 5065


Translate to English and align native-image command

  • Replace the Portuguese header and description with English, e.g.:
    “ChronDB can be distributed as a GraalVM native image:”
  • Change step 3 to mirror the workflow’s invocation, for example:
    cd target && native-image @native-image-args -H:Name=chrondb_local
🤖 Prompt for AI Agents
In docs/operations.md around lines 141 to 150, the GraalVM native-image section
is in Portuguese and the native-image invocation differs from the workflow;
translate the header and descriptive sentence to English (e.g., "ChronDB can be
distributed as a GraalVM native image:") and update step 3 to match the GitHub
Actions workflow by changing it to instruct the user to cd into the target
directory and run native-image using the @native-image-args file and the -H:Name
option to produce chrondb_local (i.e., align the CLI invocation with the
workflow).

Comment on lines 117 to 121
(when head-id
(let [tree-walk (TreeWalk. (.getRepository git))
h-idx (.addTree tree-walk (.parseTree (RevWalk. (.getRepository git)) head-id))]
(let [^Repository repo (.getRepository git)
tree-walk (TreeWalk. repo)
h-idx (.addTree tree-walk (.parseTree (RevWalk. repo) head-id))]
(.setRecursive tree-walk true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Close RevWalk in create-temporary-index (resource leak).

A new RevWalk is created but never closed. Wrap it and close in finally.

-        (let [^Repository repo (.getRepository git)
-              tree-walk (TreeWalk. repo)
-              h-idx (.addTree tree-walk (.parseTree (RevWalk. repo) head-id))]
+        (let [^Repository repo (.getRepository git)
+              tree-walk (TreeWalk. repo)
+              rev-walk (RevWalk. repo)
+              h-idx (.addTree tree-walk (.parseTree rev-walk head-id))]
           (.setRecursive tree-walk true)
           ...
-          (.close tree-walk)))
+          (.close tree-walk)
+          (.close rev-walk)))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(when head-id
(let [tree-walk (TreeWalk. (.getRepository git))
h-idx (.addTree tree-walk (.parseTree (RevWalk. (.getRepository git)) head-id))]
(let [^Repository repo (.getRepository git)
tree-walk (TreeWalk. repo)
h-idx (.addTree tree-walk (.parseTree (RevWalk. repo) head-id))]
(.setRecursive tree-walk true)
(when head-id
(let [^Repository repo (.getRepository git)
tree-walk (TreeWalk. repo)
rev-walk (RevWalk. repo)
h-idx (.addTree tree-walk
(.parseTree rev-walk head-id))]
(.setRecursive tree-walk true)
;; … existing code using tree-walk …
(.close tree-walk)
(.close rev-walk)))
🤖 Prompt for AI Agents
In src/chrondb/storage/git/commit.clj around lines 117 to 121, a RevWalk is
instantiated for parsing the tree (RevWalk. repo) but never closed, causing a
resource leak; modify the code to ensure the RevWalk is closed after use by
wrapping its creation/usage in a try/finally or using Clojure's with-open (or
equivalent) so that .close is always called after .parseTree completes, and keep
the existing TreeWalk logic intact.

- update Clojure CLI to 1.12.3.1577 in build workflow for better compatibility
- update .gitignore for GraalVM native image artifacts and META-INF/
- prep for native-image builder and Docker integration

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebbb8a6 and 0768058.

📒 Files selected for processing (2)
  • .github/workflows/build-native-image.yml (1 hunks)
  • .gitignore (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/build-native-image.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run SQL Protocol Benchmark
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (2)
.gitignore (2)

30-30: LGTM: Ignore local direnv file.

.envrc should be ignored.


33-33: LGTM: Ignore generated docs.

docs/api/ belongs in .gitignore.

Comment on lines +21 to +25
report/
.native-image/
chrondb_*
META-INF/
target/native/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Don't ignore META-INF globally — it will hide GraalVM configs.

META-INF/ matches in any directory and would exclude resources/META-INF/native-image/** (e.g., reflect-config.json, resource-config.json) requested by the native-image work. Remove it. While here, minor cleanups:

  • target/native/ is redundant (already covered by target/).
  • Scope chrondb_* to repo root to avoid unintended ignores in subdirs.

Apply within these lines:

 report/
 .native-image/
-chrondb_*
-META-INF/
-target/native/
+/chrondb_*
+# Do not ignore META-INF globally; needed for native-image configs
+# target/native/ is already covered by target/
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
report/
.native-image/
chrondb_*
META-INF/
target/native/
report/
.native-image/
/chrondb_*
# Do not ignore META-INF globally; needed for native-image configs
# target/native/ is already covered by target/
🤖 Prompt for AI Agents
In .gitignore around lines 21 to 25, remove the global "META-INF/" entry (it
hides GraalVM native-image configs under resources/META-INF/native-image/),
delete the redundant "target/native/" line (target/ already covers it), and
change the unconstrained "chrondb_*" to be repo-root scoped (prepend a leading
slash: "/chrondb_*") so it doesn't ignore similarly named files in
subdirectories; keep the other entries (report/ and .native-image/) as-is and
apply these edits within the specified lines.

- Introduce Dockerfile supporting GraalVM native-image builds targeting ChronDB native binaries
- Includes Clojure 1.12 JVM toolchain with build steps for `chrondb.jar` and native artifact
- Prepares container for production runtime with restricted user and required system libs
- exposes ChronDB ports (3000, 6379, 5432)
- updates native-image invocation to leverage generated `target/native-image-args` with proper GraalVM options for Clojure and JGit compatibility
- first step for containerized, reproducible native deployment

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
- Expanded AGENT.md to provide a detailed ChronDB Agent Guide, covering project overview, code style, development guidelines, Git-based architecture, native image/Docker workflows, and documentation standards.
- Updated Dockerfile for improved clarity, reproducibility, and alignment with build/runtime best practices:
  - Separated builder/runtime stages, with explicit installation of Clojure and toolchain components.
  - Ensured correct inclusion of resources, user permissions, data directory, and exposed ports.
- Native image build instructions clarified to support GraalVM and closed-world constraints required for JGit-based persistence.
- This change enforces Clojure idioms, preserves chronological commit immutability, and documents native- and container-related procedures in depth for both developers and operators.

No application logic was changed; all updates are developer-facing material and build infrastructure.

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
dev/chrondb/native_image.clj (2)

227-229: Don't mix -cp, -jar, and -H:Class; prefer a single mode.

Passing both -cp and -jar with -H:Class is redundant and can be confusing. For uberjar builds, -jar is sufficient.

Apply this diff to use only the jar-based invocation:

-        startup-args ["-cp" classpath
-                      "-jar" jar-path
-                      "-H:Class=chrondb.core"]
+        startup-args ["-jar" jar-path]

Based on learnings


289-293: Remove redundant fallback configuration creation.

This fallback logic creates reflect-config.json and resource-config.json only if they don't exist, but lines 287-288 already wrote these files with proper content. The fallback is redundant and should be removed.

Apply this diff:

-    (doseq [[fname contents] [["reflect-config.json" "[]"]
-                              ["resource-config.json" (json/write-str {:resources {:includes []}})]]]
-      (let [file (io/file native-config-dir fname)]
-        (when-not (.exists file)
-          (spit file contents))))
+    ))
-    nil))
🧹 Nitpick comments (2)
Dockerfile (1)

17-18: Consider adding error handling for build dependencies.

The build assumes target/native-image-args exists. If the file is missing or malformed, the native-image build will fail without a clear error message.

Consider adding a verification step:

+RUN test -f target/native-image-args || (echo "Error: native-image-args not found" && exit 1)
+
 RUN clojure -M:build -- --uberjar && \
     native-image @target/native-image-args -H:Name=chrondb
dev/chrondb/native_image.clj (1)

131-134: Clarify the purpose of the empty filter set.

The clojure-initializers function uses an empty set for filtering, which will always return an empty vector. If this is intentional (no special runtime initializers needed), consider removing the function or adding a comment explaining why the filter is empty.

If no special initializers are needed:

-(defn- clojure-initializers
-  [classes]
-  (let [targets #{}]
-    (vec (filter targets classes))))
+(defn- clojure-initializers
+  [classes]
+  ;; No special runtime initializers required for clojure.lang classes
+  [])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0768058 and b8196e5.

📒 Files selected for processing (3)
  • AGENT.md (1 hunks)
  • Dockerfile (1 hunks)
  • dev/chrondb/native_image.clj (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • AGENT.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test
  • GitHub Check: Run SQL Protocol Benchmark
  • GitHub Check: test
🔇 Additional comments (10)
Dockerfile (2)

37-37: Document the purpose of multiple exposed ports.

Exposing three ports (3000, 6379, 5432) suggests multiple protocol support. While this may be intentional, ensure each port is documented and secured appropriately, as multiple exposed ports increase the attack surface.

Consider verifying which protocols are actually implemented and whether all ports need to be exposed by default.


31-35: LGTM!

The non-root user setup follows security best practices with proper ownership and restricted shell access.

dev/chrondb/native_image.clj (8)

1-6: LGTM!

Namespace and imports are properly structured.


8-108: LGTM!

The class lists for build-time and run-time initialization are well-organized and appropriate for GraalVM native-image compilation.


112-129: LGTM!

The function correctly extracts clojure.lang.* classes from the JAR and handles the missing file case gracefully.


136-184: LGTM!

The reflection configuration properly exposes necessary constructors, methods, and fields for JGit, Lucene, and Jetty classes.


186-197: LGTM!

Helper functions are simple and correct. The macos-sdk-root function properly handles exceptions and ensure-dir! ensures directory existence.


199-226: LGTM!

The setup phase correctly creates directories, detects the macOS SDK, and processes Clojure classes to separate build-time and run-time initialization.


230-275: LGTM!

The native-image arguments are comprehensive and include appropriate debugging options, trace configurations, and security settings.


276-288: LGTM!

The reflection and resource configuration files are properly generated with appropriate patterns for JGit, Clojure, and META-INF resources.

- Added section in AGENT.md describing the new `publish-docker.yml` workflow for automated Docker image publishing to GitHub Container Registry.
- Documented authentication, build, and push details for Docker CI in project docs.
- Updated Dockerfile to use `ghcr.io/graalvm/native-image:ol9-java17-22` (removing patch version for wider compatibility with upstream image updates).
- Ensured all CI/CD and native image notes comply with project conventions and explain rationale for each pipeline change.

fixed: #57 (comment)

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
- Introduce `.github/workflows/publish-docker.yml` for automated Docker image build and publish.
- Builds using project Dockerfile and pushes latest tag to GitHub Container Registry.
- Authenticates via `${{ secrets.GITHUB_TOKEN }}` with package write permissions.
- Ensures every push to `main` branch publishes up-to-date image for streamlined deployments.

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
@avelino avelino merged commit dad3a2e into main Oct 11, 2025
9 of 10 checks passed
avelino added a commit that referenced this pull request Oct 11, 2025
- Added section in AGENT.md describing the new `publish-docker.yml` workflow for automated Docker image publishing to GitHub Container Registry.
- Documented authentication, build, and push details for Docker CI in project docs.
- Updated Dockerfile to use `ghcr.io/graalvm/native-image:ol9-java17-22` (removing patch version for wider compatibility with upstream image updates).
- Ensured all CI/CD and native image notes comply with project conventions and explain rationale for each pipeline change.

fixed: #57 (comment)

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
@avelino avelino deleted the avelino/native-image-builder branch October 11, 2025 21:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
Dockerfile (1)

1-4: Update builder image to a supported GraalVM tag

ghcr.io/graalvm/native-image:ol9-java17-22 is from 2022 and no longer receives CPU/security fixes. The supported images have moved to ghcr.io/graalvm/native-image-community, and hard-coding the 22.3.2 path in PATH blocks future upgrades. Switch to the current community image and drop the version-specific PATH to keep native-image on PATH while staying patched.

-FROM ghcr.io/graalvm/native-image:ol9-java17-22 AS builder
-
-ENV CLOJURE_VERSION=1.12.3.1577 \
-    PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/graalvm-ce-java17-22.3.2/bin:$PATH"
+FROM ghcr.io/graalvm/native-image-community:17-ol9 AS builder
+
+ENV CLOJURE_VERSION=1.12.3.1577
🧹 Nitpick comments (2)
.github/workflows/publish-docker.yml (1)

36-45: Tag images with the commit SHA for traceability

Publishing only latest makes it impossible to trace or roll back specific builds. Add a commit SHA tag so every push yields an immutable reference.

-                  tags: type=raw,value=latest
+                  tags: |
+                      type=raw,value=latest
+                      type=sha
AGENT.md (1)

124-130: Update docs to match the supported builder image

The Docker instructions still point to ghcr.io/graalvm/native-image:ol9-java17-22.3.2. Once the Dockerfile switches to the community tag, please update this section to reference ghcr.io/graalvm/native-image-community:17-ol9 so the guide reflects the actual build base.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8196e5 and ca2eb71.

📒 Files selected for processing (3)
  • .github/workflows/publish-docker.yml (1 hunks)
  • AGENT.md (1 hunks)
  • Dockerfile (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run SQL Protocol Benchmark
  • GitHub Check: test
  • GitHub Check: test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement GraalVM Native Image support for binary distribution

2 participants