Thank you for your interest in contributing to oops! This document provides guidelines and instructions for contributing.
Please be respectful and constructive in all interactions. We welcome contributors of all experience levels.
- Rust toolchain (stable, 1.88+)
- Git
-
Clone the repository:
git clone https://github.com/oops-cli/oops.git cd oops -
Build the project:
cargo build
-
Run tests:
cargo test -
Check formatting and lints:
cargo fmt --check cargo clippy
feature/description- New featuresfix/description- Bug fixesdocs/description- Documentation changesrefactor/description- Code refactoring
Follow conventional commits format:
type(scope): description
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(rules): add kubectl rulesfix(git): handle detached HEAD statedocs: update installation instructionschore: update dependencies
Important for PR titles: All merged PRs trigger a minor version bump (e.g., 0.1.0 → 0.2.0):
- Use clear, descriptive PR titles following conventional commit format
- Add
[skip release]to PR title to prevent automatic release - For major or patch bumps, manual releases can be created
- Fork the repository
- Create a feature branch
- Make your changes
- Ensure all tests pass
- Submit a pull request
Rules are the core of oops. Here's how to add a new one:
Create a new file or add to an existing module in src/rules/:
use crate::core::{Command, Rule};
/// Fixes [describe what this rule fixes]
pub struct MyNewRule;
impl Rule for MyNewRule {
fn name(&self) -> &str {
"my_new_rule"
}
fn is_match(&self, cmd: &Command) -> bool {
// Return true if this rule applies to the command
cmd.output.contains("specific error pattern")
}
fn get_new_command(&self, cmd: &Command) -> Vec<String> {
// Return the corrected command(s)
vec![format!("corrected {}", cmd.script)]
}
// Optional: Override defaults
fn priority(&self) -> i32 {
1000 // Lower = higher priority
}
fn enabled_by_default(&self) -> bool {
true
}
fn requires_output(&self) -> bool {
true // Set false if rule doesn't need command output
}
}Add your rule to src/rules/mod.rs:
rules.push(Box::new(my_module::MyNewRule));#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_my_new_rule_matches() {
let cmd = Command::new("original command", "error output");
let rule = MyNewRule;
assert!(rule.is_match(&cmd));
}
#[test]
fn test_my_new_rule_correction() {
let cmd = Command::new("original command", "error output");
let rule = MyNewRule;
let corrections = rule.get_new_command(&cmd);
assert_eq!(corrections[0], "expected correction");
}
#[test]
fn test_my_new_rule_no_match() {
let cmd = Command::new("unrelated command", "different output");
let rule = MyNewRule;
assert!(!rule.is_match(&cmd));
}
}- Be specific: Rules should match precisely, not broadly
- Test thoroughly: Include both positive and negative test cases
- Document: Add doc comments explaining what the rule does
- Consider edge cases: Handle unusual inputs gracefully
- Keep it simple: One rule, one purpose
oops/
├── src/
│ ├── main.rs # Entry point
│ ├── lib.rs # Library root
│ ├── cli.rs # CLI argument parsing
│ ├── config/ # Configuration system
│ ├── core/ # Core types (Command, Rule, etc.)
│ ├── rules/ # All correction rules
│ │ ├── git/ # Git-related rules
│ │ ├── package_managers/
│ │ └── ...
│ ├── shells/ # Shell integrations
│ ├── output/ # Command execution
│ ├── ui/ # Terminal UI
│ └── utils/ # Utilities
├── tests/ # Integration tests
├── benches/ # Benchmarks
└── docs/ # Documentation
cargo testcargo test --test cli_testscargo bench- Run
cargo fmtbefore committing - Run
cargo clippyand address warnings - Follow Rust naming conventions
- Write documentation for public APIs
- Keep functions focused and small
Releases are fully automated via GitHub Actions when a PR is merged to master.
For detailed information, see Auto-Release Workflow Documentation.
- Create a PR with your changes
- Use a clear, descriptive PR title (conventional commit format recommended):
- Examples:
feat: add new rules,fix: resolve crash,docs: update guide - All PRs trigger minor bumps: 0.1.0 → 0.2.0 → 0.3.0
- Skip release: Add
[skip release]to title
- Examples:
- Merge the PR
- The
auto-releaseworkflow will:- Run tests on Linux, macOS, and Windows
- Automatically bump the version in
Cargo.toml(minor bump) - Create and push a git tag (e.g.,
v0.1.1)
- The
releaseworkflow (triggered by tag) will:- Build binaries for 6 platforms (Linux x86_64/ARM64, macOS x86_64/ARM64, Windows x86_64)
- Generate SHA256 checksums
- Create a GitHub release with all artifacts
To skip automatic release: Include [skip release] or [no release] in your PR title.
If needed, you can manually trigger a release:
- Bump version:
cargo set-version --bump patch(or minor/major) - Commit:
git add Cargo.toml Cargo.lock && git commit -m "chore: bump version to X.Y.Z" - Tag and push:
git tag -a vX.Y.Z -m "Release vX.Y.Z" && git push origin main --tags - The release workflow will build and publish automatically
- Open an issue for bugs or feature requests
- Start a discussion for questions
- Check existing issues before creating new ones
By contributing, you agree that your contributions will be licensed under the MIT License.