# Contributing to SousChef

Thank you for contributing! This guide covers essentials for contributing to Chef-to-Ansible migration and Ansible upgrade planning tools.

## Quick Start

```bash
git clone https://github. com/kpeacocke/souschef.git && cd souschef
poetry install
poetry run pytest --cov=souschef  # All tests should pass
poetry run ruff check .            # No errors
poetry run mypy souschef           # No type errors
```

**Prerequisites:** Python 3.10+, Poetry, Git

## Architecture

**Read [ARCHITECTURE.md](docs/ARCHITECTURE.md) first!** It explains:

- Module responsibilities (parsers/, converters/, core/, etc.)
- Decision tree for where code belongs
- Design patterns and common scenarios

**Quick reference:**

- `parsers/` = Extract Chef/Ansible artifacts (read-only)
- `converters/` = Transform Chef to Ansible
- `assessment.py` = Migration planning
- `deployment.py` = AWX/AAP integration
- `ansible_upgrade.py` = Ansible upgrade planning
- `server.py` = MCP tool registration
- `cli.py` = Command-line interface

## Code Standards

### Zero Warnings Policy

All code must pass **all tools** with zero errors/warnings:

```bash
poetry run ruff check .      # Linting (MUST pass)
poetry run ruff format .     # Formatting
poetry run mypy souschef     # Type checking (MUST pass)
poetry run pytest --cov      # Tests (MUST pass, 90%+ coverage)
```

**Critical:** Never add code suppressions (`# noqa`, `# type: ignore`, etc.) without user approval. Fix the issue, don't mask it.

**Exception:** Pre-existing `# noqa: F401` for backward compatibility exports—respect these.

### Type Hints

- **Source code** (`souschef/`): Type hints required for all functions
- **Test files**: Can omit types for pytest fixtures and parameterized tests
- Use built-in types (dict, list) and `typing` module appropriately

### Docstrings

Google style, required for all functions/classes/modules:

```python
def convert_resource(name: str, action: str) -> dict:
    """Convert Chef resource to Ansible task.

    Args:
        name: Resource name.
        action: Resource action (install, create, etc.).

    Returns:
        Dictionary with Ansible task definition.

    Raises:
        ValueError: If resource type unsupported.
    """
```

### Australian English

Use Australian English spelling in all documentation, comments, and docstrings:

- **Use:** colour, centre, organise, recognise, optimise, behaviour
- **Not:** color, center, organize, recognize, optimize, behavior

## Testing Requirements

### Accessibility Regression Checklist

For UI-related changes, include an accessibility regression pass using:

- `docs/testing/ACCESSIBILITY_REGRESSION_CHECKLIST.md`

Treat this as part of the CI-facing quality evidence in your PR description.

### Three Test Types Required

**1. Unit Tests** (`tests/unit/test_server.py`)

Mock-based, fast, isolated:

```python
def test_parse_recipe_success():
    """Test recipe parsing with mocked file."""
    with patch("souschef.server.Path.read_text") as mock:
        mock.return_value = "package 'nginx'"
        result = parse_recipe("/path/to/recipe.rb")
        assert "nginx" in result
```

**2. Integration Tests** (`tests/integration/test_integration.py`)

Real files from `tests/integration/fixtures/`:

```python
def test_parse_real_recipe():
    """Test with actual Chef recipe file."""
    fixture_path = FIXTURES_DIR / "sample_cookbook" / "recipes" / "default.rb"
    result = parse_recipe(str(fixture_path))
    assert "Resource 1:" in result
```

**3. Property-Based Tests** (`tests/unit/test_property_based.py`)

Fuzz testing with Hypothesis:

```python
@given(st.text(min_size=1, max_size=100))
@settings(max_examples=50)
def test_handles_any_input(random_input):
    """Test function handles any string input."""
    result = function_name(random_input)
    assert isinstance(result, str)  # Should never crash
```

### Coverage Goal

- **Current:** 91% (913 tests passing across 13 test files)
- **Target:** 100% line and branch coverage
- **Requirement for PRs:** Coverage maintained or improved
- **For New Code:** All three test types required (unit, integration, property-based)

## Dependency Management

### Adding Dependencies

```bash
# Production dependency
poetry add package-name

# Development dependency
poetry add --group dev package-name

# With version constraints
poetry add "package-name>=1.0.0,<2.0.0"
```

### Lock File Synchronization

**Three layers of automation prevent lock file issues:**

1. **Pre-commit hooks** (automatic): Validates and regenerates lock on each commit
2. **GitHub Actions** (CI): Validates lock in all PRs
3. **Dependabot** (weekly): Automated dependency updates

**Manual update if needed:**

```bash
poetry lock  # Updates lock file
poetry check # Validates pyproject.toml and poetry.lock match
```

## Submitting Changes

### 1. Create Feature Branch

```bash
git checkout -b feature/your-feature-name
```

### 2. Make Changes

Follow code standards, add tests, update docs if needed.

### 3. Pre-Submit Checklist

Run these checks **before committing**:

```bash
# Lint and format
poetry run ruff check .
poetry run ruff format .

# Type check
poetry run mypy souschef

# Run tests
poetry run pytest --cov=souschef

# Check git status
git status  # Should be clean after commit
```

These checks are **mandatory quality gates**. Do not skip.

### 4. Commit with Conventional Commits

**IMPORTANT:** Commit messages directly control automatic version bumping. Use the correct type!

Format: `<type>(<scope>): <subject>`

**Types and automatic version impact:**

- `feat:` = New feature → **Minor version bump** (e.g., 7.2.0 → 7.3.0)
- `fix:` = Bug fix → **Patch version bump** (e.g., 7.2.0 → 7.2.1)
- `docs:` = Documentation only → **No version bump**
- `test:` = Test changes → **No version bump**
- `refactor:` = Code refactoring → **No version bump**
- `perf:` = Performance improvements → **Patch version bump** (fix category)
- `chore:` = Maintenance → **No version bump**
- `ci:` = CI/CD changes → **No version bump**

**Examples:**

```bash
# New feature (triggers 7.2.0 → 7.3.0)
git commit -m "feat(parsers): add support for Puppet DSL parsing"

# Bug fix (triggers 7.2.0 → 7.2.1)
git commit -m "fix(converters): correct indentation in generated playbooks"

# No version bump (for chores, docs, tests)
git commit -m "docs: update README with new examples"
git commit -m "test: add edge case coverage for recipe parser"
```

**Breaking changes:** Add `!` after type or `BREAKING CHANGE:` in body → **Major version bump** (e.g., 7.2.0 → 8.0.0):

```bash
git commit -m "feat!: change MCP tool parameter names

BREAKING CHANGE: Renamed 'path' parameter to 'file_path' for consistency."
```

**How automatic versioning works:**

1. You push commits to `main` branch
2. CI workflow runs and passes
3. Release workflow is triggered
4. **python-semantic-release analyzes commit history** since the last version tag
5. Version is automatically bumped based on commit types:
   - No `feat:` or `feat!:` commits + has `fix:` → patch bump
   - Has `feat:` commits → minor bump
   - Has `feat!:` or breaking changes → major bump
6. Version is updated in `pyproject.toml`
7. Package is built and released to PyPI
8. Docker images are built and pushed
9. GitHub release is created with changelog

**Result:** Versions are automatically incremented—no manual updates needed!

### 5. Create Pull Request

**PR Checklist:**

- [ ] No linting errors (`ruff check`)
- [ ] Properly formatted (`ruff format`)
- [ ] No type errors (`mypy souschef`)
- [ ] All tests pass (`pytest`)
- [ ] Coverage maintained/improved (90%+)
- [ ] Unit, integration, and property-based tests added
- [ ] Type hints complete
- [ ] Docstrings present and clear
- [ ] Error cases handled
- [ ] Cross-platform compatible (use `pathlib.Path`)
- [ ] Architecture respected (code in correct module)
- [ ] Australian English spelling

## Adding New MCP Tools

### Implementation Steps

**1. Create the tool function:**

```python
@mcp.tool()
def your_new_tool(parameter: str) -> str:
    """Brief description.

    Args:
        parameter: Description.

    Returns:
        Description of return value.

    Raises:
        ValueError: When parameter invalid.
    """
    try:
        # Implementation
        return result
    except Exception as e:
        return f"Error: {e}"
```

**2. Add to CLI** (if appropriate):

```python
@cli.command()
@click.argument("parameter", type=str)
def your_command(parameter: str) -> None:
    """CLI command description."""
    result = your_new_tool(parameter)
    click.echo(result)
```

**3. Add comprehensive tests:**

- Unit tests with mocks
- Integration tests with fixtures
- Property-based tests
- Error case coverage

**4. Update documentation:**

- Add to README.md capabilities
- Update docs/user-guide/mcp-tools.md
- Include usage examples

## Development Tips

### Working with Fixtures

- Store realistic Chef content in `tests/integration/fixtures/sample_cookbook/`
- Include edge cases
- Update when adding new parsing capabilities

### Performance Testing

```python
def test_parse_large_recipe(benchmark):
    """Benchmark parsing performance."""
    result = benchmark(parse_recipe, large_recipe_pathstr())
    assert len(result) > 0
```

### Cross-Platform

- Use `pathlib.Path` for all file operations
- Avoid OS-specific code
- Test on multiple Python versions if possible

## Resources

- [SousChef README](README.md) - Project overview
- [Architecture Guide](docs/ARCHITECTURE.md) - Code organization
- [Security Policy](SECURITY.md) - Security guidelines
- [Code of Conduct](CODE_OF_CONDUCT.md) - Community guidelines

### External Resources

- [Chef Documentation](https://docs.chef.io/)
- [Ansible Documentation](https://docs.ansible.com/)
- [Model Context Protocol](https://modelcontextprotocol.io/)
- [Python Type Hints](https://docs.python.org/3/library/typing.html)

## Getting Help

### Where to Ask

- **GitHub Issues**: Bugs, feature requests, questions
- **GitHub Discussions**: Broader discussions
- **PR Comments**: Code-specific questions

### What to Include

- Python version and OS
- SousChef version
- Complete error messages
- Minimal reproduction example
- Expected vs actual behaviour

## Recognition

Contributors are recognized in:

- GitHub contributors list
- Release notes for significant contributions
- README acknowledgments

## Release Process (Maintainers)

SousChef uses automated releases via Release Please:

- **Conventional commits** determine version bumps automatically
- **Releases triggered** when PRs merge to `main`
- **Contributors:** Target `develop` branch with PRs

**Version bumps:**

- `fix:` or `docs:` → patch (5.1.4 → 5.1.5)
- `feat:` → minor (5.1.4 → 5.2.0)
- `feat!:` or `BREAKING CHANGE:` → major (5.1.4 → 6.0.0, after 1.0.0)

## License

By contributing, you agree your contributions will be licensed under the MIT License.

---

**Thank you for contributing!** Every contribution helps make Chef-to-Ansible migrations easier for everyone.
