Skip to content

Commit ead1407

Browse files
pditommasoclaude
andcommitted
Add CLAUDE.md documentation for AI code assistance
Add comprehensive documentation for Claude Code including: - Project overview and architecture (App, Client, request/response flow) - Development commands (build, test, native compilation) - Testing guidelines using Spock framework - Implementation details (boolean flags, package types, build context) - Code modification guidelines and common patterns - Release process This file helps future Claude Code instances understand the codebase structure and development workflow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c315925 commit ead1407

1 file changed

Lines changed: 223 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Wave CLI is a command-line tool for the Wave container provisioning service. It allows users to:
8+
- Build container images on-demand from Dockerfiles or Conda/CRAN packages
9+
- Augment existing containers with additional layers
10+
- Build containers for specific platforms (linux/amd64, linux/arm64)
11+
- Push containers to registries and enable Singularity format
12+
- Mirror containers between registries
13+
- Scan containers for security vulnerabilities
14+
15+
The CLI is built using Java 17 (with Java 21 toolchain) and compiles to a native binary using GraalVM.
16+
17+
## Architecture
18+
19+
### Core Components
20+
21+
**App.java** (`io.seqera.wave.cli.App`)
22+
- Main entry point implementing `Runnable`
23+
- Uses Picocli for CLI argument parsing with extensive `@Option` annotations
24+
- Orchestrates the entire request lifecycle: validation → request creation → submission → response handling
25+
- Handles multiple input modes: containerfile, image, conda packages, CRAN packages, layers
26+
- Main flow: `main()``validateArgs()``run()``createRequest()``client().submit()`
27+
28+
**Client.java** (`io.seqera.wave.cli.Client`)
29+
- HTTP client wrapper using Java 11+ HttpClient
30+
- Implements retry logic with Failsafe library for resilient API calls
31+
- Key methods:
32+
- `submit()` - Submit container build requests
33+
- `inspect()` - Inspect container metadata
34+
- `awaitCompletion()` - Poll for build completion status
35+
- `serviceInfo()` - Get Wave service version info
36+
37+
**Request/Response Flow**
38+
1. User provides CLI options (image, containerfile, packages, etc.)
39+
2. `App` validates arguments and prepares context (build context, layers, config)
40+
3. Creates `SubmitContainerTokenRequest` with all specifications
41+
4. `Client` submits to Wave API endpoint
42+
5. Returns `SubmitContainerTokenResponse` with container image URL
43+
6. Optional: `--await` flag polls until build completes
44+
45+
### Key Packages
46+
47+
- **cli/** - Main application logic and CLI interface
48+
- **cli/model/** - Extended models wrapping Wave API types (e.g., `ContainerInspectResponseEx`)
49+
- **cli/util/** - Utilities (BuildInfo, YamlHelper, Checkers, DurationConverter)
50+
- **cli/json/** - JSON serialization using Moshi
51+
- **cli/exception/** - Custom exceptions (IllegalCliArgumentException, BadClientResponseException, etc.)
52+
- **cli/config/** - Configuration objects (RetryOpts, CondaOpts, CranOpts)
53+
54+
### GraalVM Native Image
55+
56+
The project compiles to a native binary with specific configuration:
57+
- Native image config files in `app/conf/` (reflect-config.json, jni-config.json, etc.)
58+
- These are critical for reflection-based libraries (Moshi, Picocli)
59+
- When adding new API model classes that use reflection, update `reflect-config.json`
60+
- Use `--agentlib` mode (configured in build.gradle) to auto-generate configs during development
61+
62+
## Development Commands
63+
64+
### Build and Test
65+
```bash
66+
# Compile Java sources
67+
./gradlew assemble
68+
69+
# Run all tests (Spock framework in Groovy)
70+
./gradlew test
71+
72+
# Compile and run tests
73+
./gradlew check
74+
75+
# Run a single test class
76+
./gradlew test --tests AppTest
77+
78+
# Run a single test method
79+
./gradlew test --tests "AppTest.should fail when specifying mirror registry and container file"
80+
```
81+
82+
### Native Compilation
83+
```bash
84+
# Build native binary (requires GraalVM Java 21)
85+
./gradlew app:nativeCompile
86+
87+
# Run the native binary
88+
./app/build/native/nativeCompile/wave --version
89+
90+
# Build shadow JAR (fat JAR with all dependencies)
91+
./gradlew shadowJar
92+
```
93+
94+
### Running the Application
95+
```bash
96+
# Run via Gradle
97+
./gradlew run --args="-i alpine"
98+
99+
# Run the shadow JAR
100+
java -jar app/build/libs/wave.jar -i alpine
101+
102+
# Run native binary after compilation
103+
./app/build/native/nativeCompile/wave -i alpine
104+
```
105+
106+
### Dependency Management
107+
```bash
108+
# View runtime dependencies
109+
./gradlew app:dependencies --configuration runtimeClasspath
110+
111+
# View compile dependencies
112+
./gradlew app:dependencies --configuration compileClasspath
113+
```
114+
115+
## Testing Guidelines
116+
117+
### Test Framework
118+
- Uses **Spock Framework** (Groovy-based BDD testing)
119+
- Test files in `app/src/test/groovy/` with `.groovy` extension
120+
- Main test: `AppTest.groovy` covers CLI argument validation and request creation
121+
122+
### Test Structure
123+
```groovy
124+
def 'should describe what the test does'() {
125+
given:
126+
def app = new App()
127+
String[] args = ["--option", "value"]
128+
129+
when:
130+
new CommandLine(app).parseArgs(args)
131+
app.validateArgs()
132+
133+
then:
134+
// assertions or expected exceptions
135+
}
136+
```
137+
138+
### Key Testing Patterns
139+
- Use `CommandLine.parseArgs()` to simulate CLI input
140+
- Call `app.validateArgs()` to trigger validation logic
141+
- Use `thrown()` to verify exceptions: `def e = thrown(IllegalCliArgumentException)`
142+
- Mock-free approach: tests primarily verify argument parsing and validation logic
143+
144+
## Important Implementation Details
145+
146+
### Boolean Flags vs Options
147+
- Boolean flags like `--mirror`, `--freeze`, `--singularity` do NOT take values
148+
- Correct: `--mirror`
149+
- Incorrect: `--mirror true` (causes UnmatchedArgumentException)
150+
151+
### Package Types
152+
The CLI supports three package ecosystems (mutually exclusive):
153+
- **Conda**: `--conda-package` or `--conda-file` with `CondaOpts`
154+
- **CRAN**: `--cran-package` with `CranOpts`
155+
- Each has base image and run command customization options
156+
157+
### Build Context and Layers
158+
- Build context (`--context`) requires a containerfile (`-f`)
159+
- Context and layers are packaged as gzip tar archives using `Packer` utility
160+
- Max sizes enforced: 5MB for build context, 1MB per layer, 10MB total for layers
161+
- `.dockerignore` support via `DockerIgnoreFilter`
162+
163+
### Output Formats
164+
- Default: prints only the container image URL
165+
- `--output json` or `--output yaml`: structured output
166+
- `--await`: waits for build completion, returns status in output
167+
168+
### Wave API Integration
169+
- Primary dependency: `io.seqera:wave-api` (currently 1.28.0)
170+
- Provides request/response models: `SubmitContainerTokenRequest`, `ContainerInspectRequest`, etc.
171+
- API endpoint configurable via `--wave-endpoint` (default: https://wave.seqera.io)
172+
173+
### Tower/Platform Integration
174+
- Optional Tower token (`--tower-token`) for authenticated builds
175+
- Required for `--build-repo` (persistent container storage)
176+
- Tower endpoint defaults to Seqera Platform Cloud: https://api.cloud.seqera.io
177+
178+
## Code Modification Guidelines
179+
180+
### Adding New CLI Options
181+
1. Add `@Option` field to `App.java`
182+
2. Update `validateArgs()` with validation logic
183+
3. Update `createRequest()` to pass option to Wave API
184+
4. Add tests in `AppTest.groovy`
185+
5. Update usage examples in `app/src/main/resources/io/seqera/wave/cli/usage-examples.txt`
186+
187+
### Adding New Wave API Models
188+
1. Update `wave-api` dependency version in `app/build.gradle`
189+
2. Add reflection config to `app/conf/reflect-config.json` for any classes using reflection
190+
3. Create extended model in `cli/model/` if additional logic needed (see `ContainerInspectResponseEx`)
191+
192+
### Modifying Native Image Configuration
193+
- Run application with `--agentlib:native-image-agent=config-merge-dir=app/conf/` to auto-generate configs
194+
- Manually verify and commit updated config files in `app/conf/`
195+
- Test native build after changes: `./gradlew app:nativeCompile`
196+
197+
## Common Patterns
198+
199+
### Error Handling
200+
- Use `IllegalCliArgumentException` for validation errors (caught in main and printed to stderr)
201+
- Use `BadClientResponseException` for API response errors
202+
- Use `ClientConnectionException` for network/connection issues
203+
- All exceptions exit with code 1
204+
205+
### Environment Variables
206+
- `TOWER_ACCESS_TOKEN` - default for `--tower-token`
207+
- `TOWER_API_ENDPOINT` - default for `--tower-endpoint`
208+
- `TOWER_WORKSPACE_ID` - default for `--tower-workspace-id`
209+
- `WAVE_ENDPOINT` - default for `--wave-endpoint`
210+
211+
### Duration Parsing
212+
- Custom `DurationConverter` for Picocli
213+
- Supports: `10m`, `2s`, `1h`, etc.
214+
- Used by `--await` option (default: 15 minutes)
215+
216+
217+
## Release Process
218+
219+
1. Update the `VERSION` file with a semantic version.
220+
2. Update the README with the new version number.
221+
3. Update the `changelog.txt file with changes against previous release. Use `git log --oneline v<PREVIOUS VERSION>..HEAD` to determine the changes to be added.
222+
4. Commit VERSION and changelog.txt file adding the tag `[release]` in the commit comment first line.
223+
5. Git push to upstream master branch.

0 commit comments

Comments
 (0)