Skip to content

support native (no jvm) setup#33

Merged
themoah merged 3 commits into
mainfrom
feat/graalvm
May 30, 2026
Merged

support native (no jvm) setup#33
themoah merged 3 commits into
mainfrom
feat/graalvm

Conversation

@themoah
Copy link
Copy Markdown
Owner

@themoah themoah commented May 29, 2026

No description provided.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Add GraalVM native image build support with multi-arch Docker pipeline

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add GraalVM native image support for faster startup and lower memory footprint
• Implement native build pipeline with multi-architecture Docker image support
• Create startup/memory benchmarking script for performance validation
• Add native image configuration and reachability metadata hints
Diagram
flowchart LR
  A["GraalVM Native Build"] -->|"nativeCompile"| B["Native Binary<br/>70-100ms startup<br/>44MB RSS"]
  B -->|"Dockerfile.native"| C["Multi-arch Images<br/>linux/amd64<br/>linux/arm64"]
  C -->|"Manifest merge"| D["Docker Hub & GHCR<br/>:native & :version-native"]
  E["Benchmark Script"] -->|"Validates"| B
  F["Reachability Metadata"] -->|"Configures"| A

Loading

Grey Divider

File Changes

1. scripts/benchmark-startup.sh ✨ Enhancement +59/-0

Startup and memory benchmarking script

• New bash script to benchmark startup time and memory footprint for Klag variants
• Supports both JVM (via java-home) and native binary execution modes
• Measures RSS memory usage and startup latency with configurable run counts
• Requires running Kafka instance for accurate benchmarking

scripts/benchmark-startup.sh


2. .github/workflows/build-and-push.yml ⚙️ Configuration changes +23/-1

Add native build validation workflow job

• Add new native-build job to validate GraalVM native compilation
• Uses GraalVM JDK 21 setup action for native image toolchain
• Runs gradle nativeCompile to verify metadata hints stay correct
• Job depends on test-helm-chart and runs conditionally on success/skip

.github/workflows/build-and-push.yml


3. .github/workflows/release.yml ⚙️ Configuration changes +99/-0

Add multi-arch native image release pipeline

• Export version output from release job for downstream jobs
• Add new release-native job for multi-architecture native image builds
• Implement release-native-manifest job to merge platform-specific digests
• Build native images on matching runners (amd64 on ubuntu-latest, arm64 on ubuntu-24.04-arm)
• Push images to Docker Hub and GHCR with :native and :-native tags

.github/workflows/release.yml


View more (5)
4. CLAUDE.md 📝 Documentation +21/-0

Document GraalVM native image setup and performance

• Add GraalVM native image build instructions and requirements
• Document native config location in build.gradle.kts and META-INF hints
• Include performance measurements comparing native vs JVM startup/memory
• Provide benchmark script usage and Docker build examples

CLAUDE.md


5. Dockerfile.native ⚙️ Configuration changes +34/-0

Multi-stage Dockerfile for native image builds

• New multi-stage Dockerfile for GraalVM native image builds
• Builder stage: GraalVM CE 21 with Gradle, installs build dependencies
• Compiles native binary with mostly-static linking for distroless compatibility
• Runtime stage: distroless/base image with minimal attack surface
• Exposes port 8888 for metrics endpoint

Dockerfile.native


6. README.md 📝 Documentation +17/-0

Document native image usage and performance benefits

• Add native image usage section with Docker run example
• Document performance benefits: ~70-100ms startup vs ~500ms for JVM
• Highlight memory efficiency: ~44MB RSS vs ~119MB for JVM
• Provide build instructions for local and Docker builds

README.md


7. build.gradle.kts ⚙️ Configuration changes +31/-1

Add GraalVM native image Gradle configuration

• Add GraalVM native build tools plugin (version 0.10.6)
• Update project version from 0.1.11 to 0.2.0
• Configure graalvmNative block with KlagLauncher entry point
• Set native image build arguments: no-fallback, exception stack traces, URL protocols
• Configure runtime initialization for Netty/Vert.x/logback components
• Add conditional static linking support via -PnativeStatic property
• Enable GraalVM Reachability Metadata Repository for automatic hints

build.gradle.kts


8. src/main/resources/META-INF/native-image/io.github.themoah/klag/resource-config.json ⚙️ Configuration changes +11/-0

Add native image resource configuration

• New resource configuration for GraalVM native image compilation
• Includes logback.xml, version.properties, and application.properties files
• Ensures runtime resources are available in native binary

src/main/resources/META-INF/native-image/io.github.themoah/klag/resource-config.json


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 29, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. GHCR digests never pushed ✓ Resolved 🐞 Bug ≡ Correctness
Description
release-native pushes native images by digest only to themoah/klag, but
release-native-manifest later creates manifests for ghcr.io/themoah/klag using digests that
don’t exist in GHCR, so the GHCR native release step will fail.
Code

.github/workflows/release.yml[R132-140]

Evidence
The build step is configured to push by digest only for themoah/klag, while the manifest step
explicitly tries to create tags for both Docker Hub and GHCR using those digests, which requires the
digest images to exist in each registry.

.github/workflows/release.yml[132-140]
.github/workflows/release.yml[183-191]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The native image build step pushes only to Docker Hub (`name=themoah/klag`), but the manifest step tries to create tags for both Docker Hub and GHCR using those digests. This will fail for GHCR because the referenced digest images were never pushed to `ghcr.io/themoah/klag`.

### Issue Context
- `release-native` logs into GHCR, but the `docker/build-push-action` output is configured to push only `themoah/klag`.
- `release-native-manifest` loops over `themoah/klag` and `ghcr.io/themoah/klag` and references `repo@sha256:<digest>` for each.

### Fix Focus Areas
- .github/workflows/release.yml[132-140]
- .github/workflows/release.yml[183-191]

### Suggested fix
Update the native build step to push by digest to **both** registries (e.g., configure the build-push-action to output/push `themoah/klag` and `ghcr.io/themoah/klag`), or add a second build step to push the same build to GHCR by digest. Then keep the manifest step as-is (or adjust it to only target registries that were actually pushed).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Unverified Gradle download ✓ Resolved 🐞 Bug ⛨ Security
Description
Dockerfile.native downloads and executes a Gradle distribution via curl/unzip without any
integrity verification, so a corrupted or tampered download could alter the native release build
output.
Code

Dockerfile.native[R12-16]

Evidence
The Dockerfile installs Gradle by downloading a ZIP and unzipping it without any checksum
verification step.

Dockerfile.native[12-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The native Docker build installs Gradle by downloading a ZIP over the network and executing it without verifying a checksum/signature. This reduces build integrity for release artifacts.

### Issue Context
The Docker build runs `curl ... gradle-<version>-bin.zip` and unzips it directly into `/opt/gradle`.

### Fix Focus Areas
- Dockerfile.native[9-16]

### Suggested fix
Add an integrity check step, e.g.:
- introduce `ARG GRADLE_SHA256=<expected>`
- download the zip
- run `echo "${GRADLE_SHA256}  /tmp/gradle.zip" | sha256sum -c -`
- only then unzip/install.

(Alternatively, switch to a base image that already contains a pinned Gradle installation, or vendor a verified build toolchain into the build environment.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Errors masked in caching ✓ Resolved 🐞 Bug ☼ Reliability
Description
Dockerfile.native runs gradle dependencies || true, which hides dependency/configuration
failures and can cause later nativeCompile failures with less actionable diagnostics.
Code

Dockerfile.native[R18-21]

Evidence
The Dockerfile explicitly suppresses failures for gradle dependencies, but then relies on a
subsequent gradle nativeCompile step that will require those dependencies anyway.

Dockerfile.native[18-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The Docker dependency caching layer suppresses all failures from `gradle dependencies`, masking real build issues (bad repositories, auth problems, broken build script) and pushing failures to a later layer.

### Issue Context
The build later runs `gradle nativeCompile`, so any dependency/configuration failure will still surface, just later and harder to debug.

### Fix Focus Areas
- Dockerfile.native[18-24]

### Suggested fix
Remove `|| true` so the build fails fast when dependency resolution/configuration fails. If the intent is to make the cache step non-blocking, replace it with a more explicit conditional that logs a warning and exits non-zero only for certain error types (or accept the tradeoff and keep it strict for release builds).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Benchmark busy-wait loop ✓ Resolved 🐞 Bug ➹ Performance
Description
benchmark-startup.sh busy-waits with no sleep/timeout while grepping for the readiness marker,
which can peg CPU and hang indefinitely if the marker is never printed.
Code

scripts/benchmark-startup.sh[R37-42]

Evidence
The loop repeatedly calls grep with no sleep and no elapsed-time cutoff, so it can spin at full
speed forever if the process is stuck but still running.

scripts/benchmark-startup.sh[33-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The benchmark loop continuously runs `grep` until the marker is found, without any delay or timeout. This causes unnecessary CPU usage and can hang forever if startup never reaches the marker.

### Issue Context
The loop only breaks when the log marker appears or the process exits; it does not handle the "process alive but stuck" case.

### Fix Focus Areas
- scripts/benchmark-startup.sh[37-42]

### Suggested fix
Add both:
- a small `sleep` (e.g., `sleep 0.05` or `sleep 0.1`) inside the loop to avoid busy-waiting
- a timeout (e.g., track elapsed time and fail after N seconds, printing the last log lines) to avoid infinite hangs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread .github/workflows/release.yml
@themoah themoah merged commit 3502cd1 into main May 30, 2026
10 checks passed
@themoah themoah deleted the feat/graalvm branch May 30, 2026 19:36
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.

1 participant