|
| 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. |
0 commit comments