Thank you for your interest in contributing to codemod-pilot! This document provides guidelines and instructions for contributing.
- Code of Conduct
- Getting Started
- Development Environment
- Project Structure
- Making Changes
- Commit Convention
- Pull Request Process
- Testing
- Code Style
- Adding a New Language
- Submitting a Built-in Rule
- Getting Help
This project follows the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to the project maintainers.
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/<your-username>/codemod-pilot.git cd codemod-pilot
- Set up the development environment (see below)
- Create a feature branch:
git checkout -b feat/my-feature
- Make your changes and commit them
- Push to your fork and open a Pull Request
- Rust 1.75.0 or later (install via rustup)
- Git 2.x or later
- C compiler (for tree-sitter grammar compilation)
- Linux:
build-essentialor equivalent - macOS: Xcode Command Line Tools (
xcode-select --install) - Windows: Visual Studio Build Tools
- Linux:
Run the provided setup script to configure your development environment:
# Clone the repository
git clone https://github.com/codemod-pilot/codemod-pilot.git
cd codemod-pilot
# Run the dev setup script
./scripts/setup-dev.sh
# Or manually:
rustup toolchain install stable
rustup component add rustfmt clippy
cargo build --workspace
cargo test --workspace# Build the entire workspace
cargo build --workspace
# Run all tests
cargo test --workspace
# Run tests for a specific crate
cargo test -p codemod-core
# Run a specific test
cargo test -p codemod-core -- test_name
# Check formatting
cargo fmt --all -- --check
# Run clippy lints
cargo clippy --workspace --all-targets -- -D warnings
# Run the CLI during development
cargo run -p codemod-cli -- learn --before 'foo()' --after 'bar()'
# Update snapshot tests (using insta)
cargo insta test --workspace
cargo insta reviewcodemod-pilot/
βββ crates/
β βββ codemod-core/ # Core engine
β β βββ src/
β β β βββ lib.rs # Public API
β β β βββ pattern/ # Pattern inference from examples
β β β βββ matcher/ # AST pattern matching
β β β βββ transform/ # Code transformation engine
β β β βββ rule/ # Rule parsing and serialization
β β β βββ scanner/ # File system scanning
β β βββ tests/ # Integration tests
β βββ codemod-cli/ # CLI application
β β βββ src/
β β βββ main.rs # Entry point
β β βββ commands/ # CLI subcommands
β βββ codemod-languages/ # Language adapters
β βββ src/
β βββ lib.rs # Language registry
β βββ javascript.rs # JS/TS adapter
β βββ ...
βββ rules/ # Built-in codemod rules
βββ tests/ # End-to-end integration tests
β βββ fixtures/ # Test fixture files
βββ docs/ # Documentation
βββ scripts/ # Development and CI scripts
- Create an issue describing the bug (if one doesn't exist)
- Write a failing test that reproduces the bug
- Fix the bug
- Ensure all tests pass
- Submit a PR referencing the issue
- Open a feature request issue to discuss the design
- Wait for maintainer approval before starting significant work
- Implement the feature with tests
- Update documentation as needed
- Submit a PR referencing the issue
Documentation improvements are always welcome and don't require an issue. Just submit a PR directly.
We follow Conventional Commits for commit messages. This enables automatic changelog generation and semantic versioning.
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
| Type | Description |
|---|---|
feat |
A new feature |
fix |
A bug fix |
docs |
Documentation only changes |
style |
Formatting, missing semicolons, etc. (no code change) |
refactor |
Code change that neither fixes a bug nor adds a feature |
perf |
Performance improvement |
test |
Adding or correcting tests |
build |
Changes to build system or dependencies |
ci |
Changes to CI configuration |
chore |
Other changes that don't modify src or test files |
| Scope | Description |
|---|---|
core |
Changes to codemod-core crate |
cli |
Changes to codemod-cli crate |
langs |
Changes to codemod-languages crate |
docs |
Documentation changes |
ci |
CI/CD changes |
feat(core): add multi-example pattern inference
Supports learning transformation patterns from multiple before/after
example pairs. The engine finds the common structural diff across all
examples and generalizes pattern variables accordingly.
Closes #42
fix(cli): handle empty scan results gracefully
Previously, scanning a directory with no matching files would panic.
Now it prints a helpful message and exits with code 0.
Fixes #87
-
Ensure your branch is up to date with
main:git fetch origin git rebase origin/main
-
Run the full test suite locally:
cargo test --workspace cargo fmt --all -- --check cargo clippy --workspace --all-targets -- -D warnings -
Fill out the PR template completely:
- Describe what changed and why
- Link to related issues
- Note any breaking changes
- Include screenshots/examples if relevant
-
Wait for CI checks to pass
-
Address review feedback promptly
-
Squash commits if requested by maintainers
- Small PRs (< 200 lines) are reviewed faster and more thoroughly
- If a change is large, consider splitting it into multiple PRs
- Each PR should be a single, coherent change
- Unit tests: Located alongside source code (
#[cfg(test)]modules) - Integration tests: Located in
crates/*/tests/ - Snapshot tests: Using insta for output comparison
- End-to-end tests: Located in
tests/at the workspace root
Every new feature or bug fix should include tests:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pattern_matches_simple_rename() {
let pattern = Pattern::from_example(
"fetchUser(id)",
"getUser(id)",
Language::JavaScript,
).unwrap();
let matches = pattern.find_matches("fetchUser(42)").unwrap();
assert_eq!(matches.len(), 1);
}
}We use insta for snapshot testing transformation outputs:
#[test]
fn test_transform_output() {
let result = transform(input, rule);
insta::assert_yaml_snapshot!(result);
}To update snapshots after intentional changes:
cargo insta test --workspace
cargo insta reviewWhile we don't enforce a strict coverage target, we aim for:
- All public API functions have at least one test
- All error paths have tests
- Edge cases are covered (empty input, large input, unicode, etc.)
- Follow the official Rust API Guidelines
- Use
rustfmtwith the project'srustfmt.tomlconfiguration - Use
clippywith no warnings - Prefer
thiserrorfor library errors andanyhowfor application errors - Document all public items with doc comments
- Use
logfor logging, notprintln!
- Use descriptive variable names; avoid single-letter names except in iterators
- Module names should be singular (
pattern, notpatterns) - Test function names should describe the scenario:
test_<what>_<condition>_<expected>
// Library code (codemod-core): use thiserror
#[derive(Debug, thiserror::Error)]
pub enum PatternError {
#[error("failed to parse before example: {0}")]
ParseBefore(String),
#[error("no structural diff found between before and after")]
NoDiff,
}
// Application code (codemod-cli): use anyhow
fn main() -> anyhow::Result<()> {
let pattern = Pattern::from_example(before, after)?;
Ok(())
}See docs/adding-a-language.md for the full guide.
Quick overview:
- Add the tree-sitter grammar dependency to
crates/codemod-languages/Cargo.toml - Create a new adapter file (e.g.,
src/python.rs) - Implement the
LanguageAdaptertrait - Register the language in
src/lib.rs - Add tests with representative code samples
- Update the supported languages documentation
Built-in rules live in the rules/ directory:
- Create a
.codemod.yamlfile following the rule format specification - Add at least 3 test cases in a
rules/tests/fixture file - Document the rule in the file's
descriptionfield - Submit a PR with the
ruleslabel
- Questions: Open a Discussion on GitHub
- Bugs: File an Issue with reproduction steps
- Feature Ideas: Open a Feature Request
Thank you for contributing to codemod-pilot! Every contribution, no matter how small, makes a difference.