Thank you for your interest in contributing to Simple Builders! This document provides guidelines and instructions for developing and testing the project.
- Development Setup
- Project Structure
- Development Guidelines
- Building and Testing
- Debugging
- Code Style
- Submitting Changes
- Java 17+: Required for development
- Maven 3.8+: Build tool
- Git: Version control
git clone https://github.com/java-helpers/simple-builders.git
cd simple-builders
mvn clean installThe project is organized as a multi-module Maven project:
simple-builders/
├── core/ # Core annotations and runtime utilities
├── processor/ # Annotation processor (compile-time code generation)
├── example/ # Example usage and integration tests
└── pom.xml # Parent POM
Important: The example module depends on the processor module being installed in your local Maven repository. This is because:
- The annotation processor must be available at compile-time
- The example uses
@SimpleBuilderannotations that trigger code generation - Tests in example validate the generated builders
This project uses annotation processing for code generation. Understanding this architecture is crucial:
- The
processormodule generates code at compile-time - The
examplemodule depends on the processor being installed in your local Maven repository - Tests use Google's compile-testing library, which makes compilation happen inside test code
After making code changes:
- Always run tests
- Use appropriate scope:
- Processor changes:
mvn test -pl processor - Changes affecting generation:
mvn test -pl processor,example -am
- Processor changes:
- Full validation before committing:
mvn clean test
- Use explicit string literals for expected values, not variables
- This improves readability and makes failures easier to diagnose
- ✅ Good: Use complete method bodies in assertions
assertContains(code, """ public PersonBuilder name(String name) { this.name = name; return this; } """);
- ❌ Avoid: Building assertion strings dynamically from variables
If you're changing code in the processor module:
# Test only the processor
mvn test -pl processorIf you're changing code in the example module, you must install the processor first:
# Install processor (skip its tests for speed)
mvn install -pl processor -DskipTests
# Then test example
mvn test -pl exampleFor changes affecting both processor and example:
# Option A: Use reactor with -am (also-make) flag
mvn test -pl processor,example -am
# Option B: Full clean install (safest)
mvn clean installAlways run a full build with all tests before committing:
# Clean build with all tests
mvn clean test
# Or full install
mvn clean install# Clean everything
mvn clean
# Compile without tests
mvn compile -DskipTests
# Install to local repository without tests
mvn install -DskipTests
# Run tests for specific modules
mvn test -pl processor,example
# Run a specific test class
mvn test -Dtest=BuilderProcessorTest -pl processor
# Run a single test method
mvn test -Dtest=BuilderProcessorTest#shouldGenerateBasicBuilder -pl processor
# Run tests matching a pattern
mvn test -Dtest=*ProcessorTest -pl processorIf you encounter strange compilation errors in the example module:
-
Clean the affected module:
mvn clean -pl example
-
Reinstall dependencies:
mvn clean install -pl processor -DskipTests mvn compile -pl example
-
Full clean (nuclear option):
mvn clean mvn install
If test classes can't find generated builders:
# Ensure processor is installed
mvn install -pl processor -DskipTests
# Clean and rebuild example
mvn clean compile -pl example
mvn test -pl exampleAlways run failing tests with verbose mode first to see what the annotation processor is doing:
# Debug a specific failing test
mvn test -pl processor -Dtest=YourFailingTest -Dsimplebuilder.verbose=trueThe annotation processor has its own verbose logging (different from Maven's -X flag):
# Enable for processor tests
mvn test -pl processor -Dsimplebuilder.verbose=true
# Enable for example compilation
mvn compile -pl example -Dsimplebuilder.verbose=true
# Enable for all tests
mvn test -Dsimplebuilder.verbose=trueWhat verbose output shows:
- Field discovery and type analysis
- Method parameter extraction
- Annotation processing steps
- Code generation details
- Exact error locations
- Complete generated source code (printed before assertions run)
For complete documentation, see DEBUG_LOGGING.md.
Example output:
========== Compilation Diagnostics ==========
--- NOTES ---
[DEBUG] simple-builders: Processing round started. Found 1 annotated elements.
[DEBUG] Processing element: Project
[DEBUG] ├─ Extracting builder definition from: test.Project
[DEBUG] │ ├─ Builder will be generated as: test.ProjectBuilder
[DEBUG] │ ├─ Analysing setters for finding fields
[DEBUG] │ │ ├─ Analyzing method: setName(java.lang.String)
[DEBUG] │ │ │ └─ Adding field: name (type: java.lang.String)
[DEBUG] │ └─ Processed 1 possible setters: added 1 fields, skipped 0
[DEBUG] ├─ Code generation for builder: ProjectBuilder
[DEBUG] │ ├─ Adding Methods for 4 candidates
[DEBUG] │ │ └─ 4 Methods added
[DEBUG] │ └─ Successfully generated builder: ProjectBuilder
simple-builders: Successfully generated 1 builder(s) in this processing round
=============================================
========== Generated Source Files ==========
--- ProjectBuilder.java ---
package test;
public class ProjectBuilder {
private String name;
public ProjectBuilder name(String name) {
this.name = name;
return this;
}
...
}
--- End of ProjectBuilder.java ---
=============================================
This makes it easy to compare expected vs actual generated code without needing a debugger.
Why is this important?
This is critical for debugging because annotation processing happens inside Google's compile-testing framework, making it otherwise invisible.
The project uses google-java-format for consistent code formatting.
Automatic formatting is applied during build via the fmt-maven-plugin:
# Format code automatically
mvn fmt:format
# Check formatting without modifying files
mvn fmt:check- SonarLint: We use SonarQube rules. Install the SonarLint IDE plugin for real-time feedback.
- Test Coverage: Aim for high test coverage for new features.
- JavaDoc: Public APIs should have comprehensive JavaDoc comments.
- Classes:
PascalCase(e.g.,BuilderProcessor) - Methods:
camelCase(e.g.,generateBuilder) - Constants:
UPPER_SNAKE_CASE(e.g.,DEFAULT_TIMEOUT) - Packages: lowercase (e.g.,
org.javahelpers.simple.builders)
-
Run all tests:
mvn clean test -
Check code formatting:
mvn fmt:check
-
Update documentation if needed
-
Write tests for new features
-
Fork the repository
-
Create a feature branch from
main:git checkout -b feature/your-feature-name
-
Make your changes following the code style guidelines
-
Commit with clear, descriptive messages:
git commit -m "Add feature: description of feature" -
Push to your fork:
git push origin feature/your-feature-name
-
Open a Pull Request against the
mainbranch
- Clear description: Explain what your PR does and why
- Reference issues: Link related issues (e.g., "Fixes #123")
- Keep it focused: One feature or fix per PR
- Include tests: Add tests for new functionality
- Update docs: Update README.md or other docs if needed
GitHub does not expose repository secrets to workflows triggered by pull requests from forks, so the secret-backed quality checks are handled specially:
- Codecov: the normal CI build performs the build, generated-source check,
and tests without secrets, then publishes coverage/test data as an artifact.
A follow-up workflow (
.github/workflows/fork-coverage.yml) publishes the test execution results and coverage to Codecov from the base-repo context. This is automatic and requires no action from contributors. Test execution results are uploaded even when tests fail so Codecov receives diagnostics; coverage is published only after a successful CI run. The CI result remains authoritative and cannot be made passing by an upload. - SonarCloud: after the unprivileged CI build and tests succeed, a separate
workflow (
.github/workflows/fork-sonar.yml) restores the compiled classes and coverage reports and publishes the analysis with the secret token. It is gated by thefork-ciGitHub Environment. A maintainer must approve the run (in the Actions/Environments prompt) before it executes. It does not rebuild or retest fork code with the token.
If you have questions or need help:
- Open an issue
- Check existing discussions
Thank you for contributing to Simple Builders! 🎉