Skip to content

Commit 1b3b7c4

Browse files
committed
chore(docs): add AI agent instructions
Add AGENTS.md with comprehensive guidance for AI coding agents working with this codebase, covering project overview, build commands, architecture, testing guidelines, and troubleshooting. CLAUDE.md is a symlink to AGENTS.md for Claude Code compatibility. Signed-off-by: Marc Nuri <marc@marcnuri.com>
1 parent 946a327 commit 1b3b7c4

File tree

2 files changed

+266
-0
lines changed

2 files changed

+266
-0
lines changed

AGENTS.md

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
# helm-java - AI Agents Instructions
2+
3+
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
4+
5+
This file provides guidance to AI coding agents (GitHub Copilot, Claude Code, etc.) when working with code in this repository.
6+
7+
## Project Overview
8+
9+
helm-java is a Java client library that allows running Helm commands directly from Java code without requiring a separate Helm CLI installation. It uses JNA (Java Native Access) to call native Go Helm libraries compiled as shared libraries for multiple platforms (darwin-amd64, darwin-arm64, linux-amd64, linux-arm64, windows-amd64). The library provides a fluent API that mirrors Helm CLI commands like `install`, `upgrade`, `uninstall`, `template`, `package`, `lint`, `repo`, `registry`, etc.
10+
11+
## Working Effectively
12+
13+
### Bootstrap and Setup
14+
15+
```bash
16+
# Clone and enter the project
17+
git clone https://github.com/manusa/helm-java.git
18+
cd helm-java
19+
20+
# Build native libraries first (required before Maven build)
21+
# Native binaries must exist in native/out/ before Maven enforcer will pass
22+
cd native
23+
go build -buildmode=c-shared -o out/helm-darwin-10.12-amd64.dylib .
24+
# (or the appropriate target for your platform)
25+
cd ..
26+
```
27+
28+
### Build Commands
29+
30+
```bash
31+
# Full build with tests (requires native binaries in native/out/)
32+
./mvnw clean install
33+
34+
# Quick build without tests
35+
./mvnw clean install -Dquickly
36+
37+
# Build specific module
38+
./mvnw clean install -pl helm-java -am
39+
40+
# Build with javadoc and sources
41+
./mvnw clean package
42+
```
43+
44+
### Testing
45+
46+
**IMPORTANT**: Tests use Testcontainers with KinD (Kubernetes in Docker) for integration tests. These tests spawn actual Kubernetes clusters and may take significant time.
47+
48+
```bash
49+
# Run all tests
50+
./mvnw test
51+
52+
# Run tests in the main helm-java module only
53+
./mvnw test -pl helm-java
54+
55+
# Run a specific test class
56+
./mvnw test -pl helm-java -Dtest=HelmInstallTest
57+
58+
# Run a specific test method
59+
./mvnw test -pl helm-java -Dtest=HelmInstallTest#withName
60+
```
61+
62+
**NEVER CANCEL** tests that involve Kubernetes operations - they may leave resources in an inconsistent state.
63+
64+
### Running the Application
65+
66+
This is a library, not a standalone application. Use it in your Java project:
67+
68+
```java
69+
// Example: Create and install a chart
70+
Helm helm = new Helm(Paths.get("path/to/chart"));
71+
Release release = helm.install()
72+
.withKubeConfig(kubeConfigPath)
73+
.withName("my-release")
74+
.call();
75+
```
76+
77+
## Architecture
78+
79+
### Technical Structure
80+
81+
```
82+
helm-java/
83+
├── helm-java/ # Main client library (public API)
84+
│ └── src/
85+
│ ├── main/java/com/marcnuri/helm/
86+
│ │ ├── Helm.java # Main entry point
87+
│ │ ├── *Command.java # Command implementations (InstallCommand, etc.)
88+
│ │ └── *.java # Result types (Release, Repository, etc.)
89+
│ └── test/java/ # JUnit 5 tests with Testcontainers
90+
├── lib/
91+
│ ├── api/ # JNA interface definitions (HelmLib, NativeLibrary)
92+
│ ├── darwin-amd64/ # macOS Intel native library wrapper
93+
│ ├── darwin-arm64/ # macOS Apple Silicon native library wrapper
94+
│ ├── linux-amd64/ # Linux x64 native library wrapper
95+
│ ├── linux-arm64/ # Linux ARM64 native library wrapper
96+
│ └── windows-amd64/ # Windows x64 native library wrapper
97+
├── native/ # Go source code that wraps Helm SDK
98+
│ ├── main.go # CGO exports for JNA
99+
│ ├── main_test.go # Go tests
100+
│ ├── go.mod # Go module dependencies
101+
│ └── out/ # Compiled native libraries (.dylib, .so, .dll)
102+
├── scripts/ # Utility scripts
103+
└── pom.xml # Parent POM (multi-module Maven project)
104+
```
105+
106+
### Design Patterns
107+
108+
1. **Fluent Builder Pattern**: All commands use method chaining for configuration
109+
```java
110+
helm.install()
111+
.withName("release")
112+
.withNamespace("namespace")
113+
.createNamespace()
114+
.waitReady()
115+
.call();
116+
```
117+
118+
2. **Native Bridge via JNA**: `HelmLib` interface defines native method signatures; platform-specific modules contain the actual `.dylib`/`.so`/`.dll` files
119+
120+
3. **Lazy Initialization**: Native library loaded on first use via `HelmLibHolder.INSTANCE`
121+
122+
4. **Options Structs**: Go code defines C structs that map to Java options classes in `lib/api`
123+
124+
## Code Style
125+
126+
### Java
127+
128+
- Java 8 compatibility required (`maven.compiler.source=1.8`)
129+
- Apache License 2.0 header on all source files
130+
- Use AssertJ for assertions in tests
131+
- No external mocking frameworks - use real implementations
132+
133+
### Go (native/)
134+
135+
- Standard Go formatting (`go fmt`)
136+
- CGO exports with `//export` comments
137+
- C structs defined in comments for JNA interop
138+
139+
### Naming Conventions
140+
141+
- Command classes: `{Verb}Command.java` (e.g., `InstallCommand`, `UpgradeCommand`)
142+
- Test classes: `Helm{Feature}Test.java` (e.g., `HelmInstallTest`, `HelmKubernetesTest`)
143+
- JNI options: `{Operation}Options.java` (e.g., `InstallOptions`, `LintOptions`)
144+
145+
## Testing Guidelines
146+
147+
### Philosophy
148+
149+
1. **Black-box Testing**: Tests verify behavior and observable outcomes, not implementation details. Test the public API only.
150+
151+
2. **Avoid Mocks**: Use real implementations and test infrastructure whenever possible. The project uses Testcontainers with KinD for Kubernetes integration tests.
152+
153+
3. **Nested Test Structure**: Use JUnit 5 `@Nested` annotations with inner classes to organize tests by scenario.
154+
155+
4. **Scenario-Based Setup**: Define common scenario in the outer `@BeforeEach`; define specific conditions in nested class setup.
156+
157+
5. **Single Assertion Per Test**: Each test block should assert ONE specific condition for clear failure identification.
158+
159+
### Test Structure Example
160+
161+
```java
162+
class HelmFeatureTest {
163+
private Helm helm;
164+
165+
@BeforeEach
166+
void setUp(@TempDir Path tempDir) {
167+
helm = Helm.create().withName("test").withDir(tempDir).call();
168+
}
169+
170+
@Nested
171+
class Install {
172+
@Nested
173+
class Valid {
174+
@Test
175+
void withName() {
176+
final Release result = helm.install()
177+
.clientOnly()
178+
.withName("test")
179+
.call();
180+
assertThat(result)
181+
.returns("test", Release::getName)
182+
.returns("deployed", Release::getStatus);
183+
}
184+
}
185+
186+
@Nested
187+
class Invalid {
188+
@Test
189+
void withMissingChart() {
190+
final InstallCommand install = Helm.install("/tmp/nothing")
191+
.clientOnly()
192+
.withName("test");
193+
assertThatThrownBy(install::call)
194+
.message()
195+
.contains("not found");
196+
}
197+
}
198+
}
199+
}
200+
```
201+
202+
### Kubernetes Integration Tests
203+
204+
- `HelmKubernetesTest` uses KinD via Testcontainers
205+
- `@BeforeAll` starts the KinD cluster (expensive operation)
206+
- Tests use `kubeConfigFile` or `kubeConfigContents` for cluster access
207+
- Use `@AfterEach` to clean up releases to avoid test pollution
208+
209+
## Common Tasks
210+
211+
### Adding a New Helm Command
212+
213+
1. Create options class in `lib/api`: `lib/api/src/main/java/com/marcnuri/helm/jni/{Operation}Options.java`
214+
2. Add C struct definition in `native/main.go`
215+
3. Implement Go function with `//export` in `native/main.go`
216+
4. Add method to `HelmLib` interface in `lib/api`
217+
5. Create command class in `helm-java`: `helm-java/src/main/java/com/marcnuri/helm/{Operation}Command.java`
218+
6. Add factory method in `Helm.java`
219+
7. Write tests in `helm-java/src/test/java/com/marcnuri/helm/Helm{Feature}Test.java`
220+
221+
### Updating Native Library
222+
223+
```bash
224+
cd native
225+
# Make changes to main.go
226+
go test ./... # Run Go tests first
227+
# Build for your platform
228+
go build -buildmode=c-shared -o out/helm-darwin-10.12-arm64.dylib .
229+
cd ..
230+
./mvnw test -pl helm-java
231+
```
232+
233+
### Debugging Native Calls
234+
235+
Enable debug output in commands:
236+
```java
237+
helm.install().debug().withName("test").call();
238+
```
239+
240+
## Troubleshooting
241+
242+
### Native library not found
243+
244+
Ensure the native binaries exist in `native/out/` before running Maven. The enforcer plugin requires all platform binaries to exist:
245+
- `helm-darwin-10.12-amd64.dylib`
246+
- `helm-darwin-10.12-arm64.dylib`
247+
- `helm-linux-amd64.so`
248+
- `helm-linux-arm64.so`
249+
- `helm-windows-4.0-amd64.dll`
250+
251+
### Tests fail with "KUBECONFIG" errors
252+
253+
The Surefire plugin is configured with `KUBECONFIG=/dev/null` to prevent tests from using your local kubeconfig. This is intentional.
254+
255+
### KinD/Testcontainers tests hang
256+
257+
Ensure Docker is running and has sufficient resources. KinD requires a working Docker environment. On macOS, increase Docker Desktop memory allocation if tests fail with OOM.
258+
259+
### Go build fails
260+
261+
Check Go version (`go version`) matches `go.mod` requirement (Go 1.25.0+). Run `go mod tidy` to sync dependencies.
262+
263+
### Windows-specific issues
264+
265+
Building native libraries on Windows requires MinGW or similar CGO-compatible toolchain.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

0 commit comments

Comments
 (0)