Contributing to Mooncake¶
Thanks for your interest in contributing to Mooncake! ๐
Ways to Contribute¶
- ๐ Report bugs
- ๐ก Suggest features
- ๐ Improve documentation
- ๐งช Add tests
- โจ Implement features
- ๐ Create examples
- ๐จ Improve UX
Getting Started¶
Development Setup¶
-
Clone the repository
-
Install dependencies
-
Run tests
-
Run with coverage
-
Build locally
Project Structure¶
mooncake/
โโโ cmd/
โ โโโ mooncake.go # CLI entry point
โโโ internal/
โ โโโ config/ # Configuration parsing and validation
โ โโโ executor/ # Step execution logic
โ โโโ expression/ # Condition evaluation
โ โโโ facts/ # System information collection
โ โโโ filetree/ # File tree iteration
โ โโโ logger/ # Logging and TUI
โ โโโ pathutil/ # Path resolution
โ โโโ template/ # Template rendering
โโโ examples/ # Example configurations
โโโ README.md # Main documentation
โโโ ROADMAP.md # Feature roadmap
โโโ CONTRIBUTING.md # This file
Contribution Workflow¶
1. Find or Create an Issue¶
- Check existing issues
- For bugs: describe steps to reproduce
- For features: explain use case and proposed solution
- Wait for discussion/approval before starting work
2. Fork and Branch¶
# Fork the repo on GitHub, then:
git clone https://github.com/YOUR_USERNAME/mooncake.git
cd mooncake
git checkout -b feature/your-feature-name
Branch naming:
- feature/description - New features
- fix/description - Bug fixes
- docs/description - Documentation
- test/description - Tests only
3. Make Your Changes¶
Write good commit messages:
Add support for with_dict loop iteration
- Implement DictIterator in filetree package
- Add with_dict handling in executor
- Add tests for dict iteration
- Update documentation with examples
Follow Go conventions:
- Run go fmt ./...
- Run go vet ./...
- Add tests for new code
- Update documentation
Keep commits focused: - One logical change per commit - Separate refactoring from features - Separate tests from implementation
4. Add Tests¶
All new features must include tests:
// internal/executor/executor_test.go
func TestWithDict(t *testing.T) {
// Arrange
config := []config.Step{
{
Name: "Test dict iteration",
Shell: pointer("echo {{item.key}}: {{item.value}}"),
WithDict: pointer("{{my_dict}}"),
},
}
// Act
result := Execute(config, context)
// Assert
assert.NoError(t, result.Error)
assert.Equal(t, 3, result.StepsExecuted)
}
Test coverage: - Aim for 80%+ coverage on new code - Test happy path and error cases - Test edge cases
5. Update Documentation¶
If your change affects users:
- Update README.md
- Add example in examples/
- Add entry to ROADMAP.md (if feature)
- Update relevant example READMEs
6. Submit Pull Request¶
Then create a PR on GitHub with:
Title: Clear, concise description
Description template:
## What does this PR do?
Adds support for iterating over dictionaries using with_dict.
## Why is this needed?
Users often need to iterate over key-value pairs, currently only
list iteration is supported.
## How was it implemented?
- Added DictIterator in filetree package
- Extended executor to handle with_dict
- Added comprehensive tests
## Examples
\`\`\`yaml
- vars:
ports:
web: 80
api: 8080
admin: 9000
- name: Configure port
shell: echo "{{item.key}} runs on port {{item.value}}"
with_dict: "{{ports}}"
\`\`\`
## Testing
- [x] Added unit tests
- [x] Tested manually with examples
- [x] Updated documentation
## Checklist
- [x] Tests pass
- [x] Code formatted (`go fmt`)
- [x] Documentation updated
- [x] Example added
Code Style¶
Go Style Guide¶
Follow Effective Go and Go Code Review Comments.
Key points:
- Use gofmt for formatting
- Keep functions small and focused
- Write clear, descriptive names
- Add comments for exported functions
- Use early returns to reduce nesting
Example:
// ExecuteStep executes a single configuration step within the given execution context.
// It validates the step, checks skip conditions, and dispatches to the appropriate handler.
func ExecuteStep(step config.Step, ec *ExecutionContext) error {
// Validate step configuration
if err := step.Validate(); err != nil {
return err
}
// Check if step should be skipped
shouldSkip, skipReason, err := checkSkipConditions(step, ec)
if err != nil {
return err
}
if shouldSkip {
logSkipped(step, skipReason, ec)
return nil
}
// Execute the step
return dispatchStepAction(step, ec)
}
Configuration Style¶
When adding examples: - Use clear, descriptive names - Add comments explaining non-obvious choices - Keep examples focused on one feature - Test examples before committing
Testing Guidelines¶
Unit Tests¶
# Run all tests
go test ./...
# Run specific package
go test ./internal/executor
# Run with coverage
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out
# Run with race detector
go test ./... -race
Integration Tests¶
Add integration tests in internal/executor/executor_test.go for:
- End-to-end workflows
- Interaction between features
- Real file system operations
Example Testing¶
Before submitting:
# Test examples work
mooncake run --config examples/01-hello-world/config.yml --dry-run
mooncake run --config examples/05-templates/config.yml --dry-run
# Test all examples
for example in examples/*/config.yml; do
echo "Testing $example"
mooncake run --config $example --dry-run || exit 1
done
Documentation Guidelines¶
README Updates¶
When updating README.md: - Maintain existing structure - Use clear, concise language - Include code examples - Link to detailed examples - Test all code examples
Example Documentation¶
Each example should have: - README.md with clear explanation - "What You'll Learn" section - "Quick Start" commands - "Key Concepts" section - Working configuration that can be run
Code Comments¶
// Good: Explains why
// Use nested execution context to isolate loop variables
curEc := ec.Copy()
// Bad: Explains what (obvious from code)
// Copy the execution context
curEc := ec.Copy()
Feature Proposals¶
For significant features, create a proposal in docs/proposals/:
# Proposal: With Dict Iteration
## Problem
Users need to iterate over dictionaries (key-value pairs) but currently
only list iteration is supported with with_items.
## Proposed Solution
Add `with_dict` that iterates over dictionaries, providing `item.key`
and `item.value` in each iteration.
## Design
### Configuration Syntax
\`\`\`yaml
- vars:
ports:
web: 80
api: 8080
- name: Configure port
shell: echo "{{item.key}}: {{item.value}}"
with_dict: "{{ports}}"
\`\`\`
### Implementation
1. Add WithDict field to Step struct
2. Implement dict iteration in executor
3. Add tests
### Alternatives Considered
1. Extend with_items to handle dicts - Rejected, too implicit
2. Use template filters - Rejected, not ergonomic
## Open Questions
- Should we support nested dicts?
- What about empty dicts?
Pull Request Review Process¶
- Automated checks - CI must pass
- Code review - Maintainer reviews code
- Documentation review - Check docs updated
- Testing verification - Verify tests adequate
- Final approval - Merge when approved
Review criteria: - Code quality and style - Test coverage - Documentation completeness - Backward compatibility - Performance impact
Community Guidelines¶
- Be respectful and constructive
- Welcome newcomers
- Help others learn
- Focus on the problem, not the person
- Assume good intent
Getting Help¶
- Questions: Open a discussion
- Bugs: Open an issue
- GitHub Discussions: Ask questions and share ideas
Recognition¶
Contributors are recognized in: - Git commit history - Release notes - CONTRIBUTORS.md (if we create it)
Thank you for contributing to Mooncake! ๐