First off, thank you for considering contributing to FerrisScript! 🦀 It's people like you that make FerrisScript a great tool for bringing Rust-like syntax to Godot development.
- Code of Conduct
- What Should I Know Before I Get Started?
- How Can I Contribute?
- Development Environment Setup
- Development Workflow
- Pull Request Process
- Code Style Guidelines
- Testing Guidelines
- First-Time Contributors
- Community
This project and everyone participating in it is governed by the FerrisScript Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to the project maintainers.
FerrisScript is a Rust-inspired scripting language designed specifically for the Godot game engine. It brings Rust's powerful type system and ownership concepts to game scripting while maintaining ease of use. The project consists of:
- Compiler (
crates/compiler/): Lexer, parser, AST, and type checker - Runtime (
crates/runtime/): Execution environment for FerrisScript code - Godot Binding (
crates/godot_bind/): Integration layer with Godot 4.x
- v0.0.1: Released October 2, 2025 - Initial compiler and runtime
- v0.0.2: Released - Documentation improvements and community standards
- v0.0.3: Released - VS Code extension and error handling improvements
- v0.0.4: Current (October 10, 2025) - @export annotations, signals, struct literals, node queries, testing infrastructure (843 tests)
- v0.0.5: Planned - LSP alpha, arrays, for loops
FerrisScript uses the .ferris file extension for all script files (not .rscr).
Before creating bug reports, please check the existing issues to avoid duplicates.
When creating a bug report, please include:
- A clear and descriptive title
- Steps to reproduce the issue
- Expected behavior vs actual behavior
- Code samples demonstrating the issue (if applicable)
- Environment details: OS, Rust version, Godot version
- Error messages or stack traces
Use the bug report template when creating your issue.
Feature suggestions are welcome! Before suggesting a feature:
- Check if it's already been suggested or implemented
- Consider if it aligns with FerrisScript's goals (Rust-like syntax for Godot)
- Think about how it would benefit the community
When suggesting a feature, please include:
- A clear and descriptive title
- Detailed description of the proposed feature
- Use cases showing when/why this would be helpful
- Code examples showing how the feature would be used
- Alternative solutions you've considered
Use the feature request template when creating your issue.
FerrisScript uses a comprehensive label system to organize issues and pull requests. Understanding these labels helps you find tasks that match your interests and skill level.
Priority Labels (urgency):
P0-Critical🔴 - Critical bugs or blockers requiring immediate attentionP1-High🟠 - High priority tasks that should be addressed soonP2-Medium🟡 - Medium priority tasks for regular workflowP3-Low🟢 - Low priority tasks or nice-to-have improvements
Type Labels (nature of work):
bug🐛 - Something isn't working correctlyfeature✨ - New feature or functionality requestdocumentation📖 - Documentation improvements or additionsenhancement⚡ - Improvement to existing functionalityquestion❓ - Questions or clarifications neededdiscussion💬 - General discussion topics
Status Labels (current state):
needs-triage🏷️ - New issue awaiting initial reviewin-progress🚧 - Work is actively being doneblocked🚫 - Blocked by external dependencieswontfix⛔ - Issue will not be addressed (with explanation)
Difficulty Labels (skill level):
good-first-issue🌱 - Good for newcomers to the projectintermediate🌿 - Requires moderate knowledge of codebaseadvanced🌳 - Requires deep understanding of architecture
Component Labels (codebase area):
compiler- Related to lexer, parser, type checkerruntime- Related to execution environmentgodot-bind- Related to Godot GDExtension bindingsdocs- Related to documentation (not code)ci- Related to CI/CD, GitHub Actions, workflows
Finding Issues to Work On:
# Good for beginners
https://github.com/dev-parkins/FerrisScript/issues?q=is:open+label:good-first-issue
# Documentation improvements
https://github.com/dev-parkins/FerrisScript/issues?q=is:open+label:documentation
# High priority bugs
https://github.com/dev-parkins/FerrisScript/issues?q=is:open+label:P1-High+label:bug
# Compiler-related work
https://github.com/dev-parkins/FerrisScript/issues?q=is:open+label:compiler
When Creating Issues:
- Maintainers will add appropriate labels during triage
- You can suggest labels in your issue description
- Labels help us prioritize and organize work
When Working on Issues:
- Check labels to understand priority and scope
- Look for
good-first-issueif you're new - Issues with
needs-triagemay need more discussion
For complete label documentation, see docs/GITHUB_LABELS.md.
Documentation improvements are always appreciated! This includes:
- Fixing typos or unclear explanations
- Adding examples to existing documentation
- Writing tutorials or guides
- Improving code comments
- Translating documentation (future)
Important: Before contributing documentation:
- Review the Anti-Duplication Matrix to ensure you're editing the primary location for content
- Link to existing documentation rather than duplicating it
- Check DOCUMENTATION_ORGANIZATION.md for where new docs should live
- Run documentation linting locally before pushing (see Documentation Quality Checks below)
Use the documentation template when creating documentation-related issues.
IMPORTANT: Always run documentation linting locally before pushing to catch issues early!
We use automated tools to ensure documentation quality:
- markdownlint: Checks markdown formatting consistency
- markdown-link-check: Verifies all links work (internal and external)
Quick Setup (first time only):
npm installBefore Every Documentation Commit:
# Check markdown formatting (style/syntax)
npm run docs:lint
# Auto-fix formatting issues
npm run docs:fix
# Check for broken links (optional, but recommended)
npx markdown-link-check your-file.mdWhat npm run docs:lint Checks:
- ✅ Heading hierarchy and style
- ✅ List formatting consistency
- ✅ Code block formatting
- ✅ Line length (soft limit: 120 chars)
- ✅ Trailing whitespace
Note: Link checking is done automatically in CI, but you can check individual files locally with markdown-link-check if needed.
- ✅ External links (with retries)
CI Integration: These same checks run automatically on pull requests. Catching issues locally saves review time!
See scripts/README.md for detailed documentation linting guide.
Code contributions are welcome for:
- Bug fixes
- New features (after discussion in an issue)
- Performance improvements
- Test coverage improvements
- Refactoring for better maintainability
Before starting work on code, please:
- Open an issue (or comment on an existing one) to discuss your approach
- Wait for feedback from maintainers to ensure alignment
- Create a feature branch from
mainfor your work (e.g.,feature/my-feature)
When adding new language features (keywords, operators, types, syntax constructs), you must update the VS Code syntax highlighting:
- Edit
extensions/vscode/syntaxes/ferrisscript.tmLanguage.jsonto add the new patterns - Test the highlighting on
.ferrisexample files - Update
extensions/vscode/CHANGELOG.mdto document the changes - Consider adding relevant code snippets to
extensions/vscode/snippets/ferrisscript.json
See SYNTAX_HIGHLIGHTING_MAINTENANCE.md for detailed instructions, examples, and a quarterly audit checklist.
Before you begin, ensure you have:
- Rust 1.70+ (we use 1.90.0 in development)
- Git for version control
- A text editor or IDE (VS Code with rust-analyzer recommended)
For detailed installation instructions, see the README.md Installation section.
Do not duplicate installation instructions here - always link to the README.md as the single source of truth.
-
Fork and clone the repository:
# Fork via GitHub UI, then: git clone https://github.com/YOUR_USERNAME/FerrisScript.git cd FerrisScript
-
Add the upstream remote:
git remote add upstream https://github.com/dev-parkins/FerrisScript.git
-
Build the project:
cargo build
-
Run the tests to verify your setup:
cargo test
All 96 tests should pass. If they don't, please open an issue.
To verify your environment setup, try running an example:
# Build the project
cargo build --release
# Run an example
cargo run --bin rustyscript_runtime examples/hello.ferrisYou should see "Hello from FerrisScript!" printed to the console.
As of October 2025, FerrisScript uses a direct-to-main workflow (GitHub Flow) with two branch types:
-
main: Production-ready code, protected- Accepts PRs from
feature/*branches - Requires code review and passing CI
- Triggers release workflows on tags
- Accepts PRs from
-
feature/*: Individual feature branches- Created from
main - Quick CI checks for fast feedback (2-3 min)
- Full test suite runs before merge to main
- One feature per branch
- Created from
# Start from main
git checkout main
git pull origin main
# Create your feature branch
git checkout -b feature/your-feature-name
# Work on your changes
# ... make changes ...
# Test locally (same checks as CI)
cargo test --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
# Push and create PR to main
git push -u origin feature/your-feature-name
gh pr create --base main --title "feat: Your feature description"Feature Branch PRs (feature/* → main):
- ⚡ Quick Check (2-3 minutes):
- Code formatting (
cargo fmt) - Linting (
cargo clippy) - Unit tests only (Ubuntu)
- Code formatting (
- 🎯 Goal: Fast feedback during development
- 💰 Savings: ~60-70% CI time vs full suite
Main Branch (after PR merge):
- 🔄 Full Test Suite (~10-15 minutes):
- Cross-platform tests (Linux, Windows, macOS)
- All tests (unit + integration)
- Code coverage reporting
- Release builds
- 🎯 Goal: Production validation
Tagged Releases (v* tags on main):
- ✅ Full Test Suite + Release Build:
- All validation checks
- Creates GitHub release
- Attaches platform-specific binaries
- 🎯 Goal: Automated releases
If you only change documentation files, CI will be skipped entirely on feature branches:
Skipped paths:
docs/***.mdfilesLICENSE.gitignore
This saves ~95% CI time for documentation PRs!
# Feature development (direct to main)
feature/my-feature → main (via PR)
feature/another-feature → main (via PR)
# Tag for release when ready
git tag -a v0.0.5 -m "Release v0.0.5"
git push origin v0.0.5
# Creates release automatically
main (tagged) → GitHub ReleaseWe use a feature branch workflow with squash and merge strategy.
We use branch name prefixes to automatically apply the appropriate PR template:
| Prefix | Use For | PR Template Applied |
|---|---|---|
bugfix/ or fix/ |
Bug fixes | 🐛 Bug Fix Template |
feature/ or feat/ |
New features | ✨ Feature Template |
docs/ or doc/ |
Documentation | 📝 Documentation Template |
Examples:
git checkout -b bugfix/parser-null-pointer
git checkout -b feature/async-script-loading
git checkout -b docs/add-api-examples💡 Tip: When you create a PR, our automation will detect your branch name and automatically apply the appropriate template!
-
Create a feature branch from
mainwith the appropriate prefix:# Start from main git checkout main git pull origin main # For bug fixes git checkout -b bugfix/your-bug-description # For new features git checkout -b feature/your-feature-name # For documentation git checkout -b docs/your-doc-update
-
Make your changes in small, logical commits:
git add . git commit -m "feat: add new feature" # or git commit -m "fix: resolve issue with parser"
-
Keep your branch up to date with
main:git fetch origin git rebase origin/main
-
Push your branch:
git push origin feature/your-feature-name
-
Open a Pull Request to
mainvia GitHub:- Base branch:
main - Use a clear, descriptive title following Conventional Commits
- The appropriate PR template will be automatically applied based on your branch name
- Fill out all sections marked with
<!-- ... -->comments - Reference related issues (e.g., "Closes #42", "Fixes #123")
- Describe what changed and why
- Base branch:
We have specialized templates for different PR types:
-
🐛 Bug Fix (bug_fix.md)
- Focus: Root cause, regression testing, before/after comparison
- Auto-applied for:
bugfix/*orfix/*branches
-
✨ Feature (feature.md)
- Focus: Motivation, usage examples, breaking changes, performance
- Auto-applied for:
feature/*orfeat/*branches
-
📝 Documentation (docs.md)
- Focus: Markdown linting, link checking, code example testing
- Auto-applied for:
docs/*ordoc/*branches
Manual Selection: You can also manually choose a template when creating a PR via the GitHub dropdown menu.
Before your PR can be merged:
- ✅ All tests must pass (
cargo test --workspace) - ✅ Code must be formatted (
cargo fmt --all) - ✅ Code must pass strict linting (
cargo clippy --workspace --all-targets --all-features -- -D warnings) - ✅ Documentation linting passes (
npm run docs:lint) - ✅ Documentation must be updated (if applicable)
- ✅ CHANGELOG.md must be updated (see below)
- ✅ At least one maintainer approval
- Feature branches → main: We use squash and merge to keep main branch history clean and linear
- Hotfix branches: We use squash and merge for consistency
- Branch deletion: Branches are automatically deleted after merge (enable in your fork's settings)
You can open a PR early as a draft to get feedback:
- Mark as draft in the PR creation UI
- Or add
[WIP]to the title - Add a "Notes to Reviewers" section explaining current status
For significant changes, update the [Unreleased] section in CHANGELOG.md:
## [Unreleased]
### Added
- Your new feature description
### Fixed
- Your bug fix description
### Changed
- Your modification descriptionFollow the Keep a Changelog format.
We follow standard Rust conventions:
- Formatting: Use
cargo fmtbefore committing - Linting: Fix all
cargo clippywarnings - Naming:
snake_casefor functions, variables, modulesPascalCasefor types, traits, enumsSCREAMING_SNAKE_CASEfor constants
- Comments:
- Use
///for public API documentation - Use
//for inline comments - Explain "why", not "what"
- Use
- Keep functions small and focused (one responsibility)
- Group related functionality into modules
- Use meaningful variable and function names
- Prefer explicit over implicit
We follow Conventional Commits:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Formatting, missing semi-colons, etc.refactor: Code restructuring without behavior changetest: Adding or updating testschore: Maintenance tasks
Examples:
feat(parser): add support for match expressions
fix(lexer): handle escaped quotes in strings
docs: update installation instructions for Windows
📚 See docs/testing/README.md for comprehensive testing documentation
FerrisScript uses a 4-layer testing strategy to ensure quality:
- Unit Tests (Rust) - Pure logic testing (compiler/runtime)
- Integration Tests (.ferris) - End-to-end testing with Godot
- GDExtension Tests - Godot bindings requiring runtime
- Benchmark Tests - Performance measurement
Current Status: 843+ tests across all layers | ~82% code coverage
Required for all contributions:
- ✅ Every new feature must include tests
- ✅ Bug fixes must include regression tests
- ✅ Tests must pass before PR is merged
- ✅ Use descriptive test names:
test_parser_handles_nested_functions
Test placement:
- Unit tests:
#[cfg(test)] mod testsin same file - Integration tests:
.ferrisscripts ingodot_test/scripts/ - Benchmarks:
benches/directory in relevant crate
Quick Links:
- Testing Guide - Complete testing patterns ⭐ START HERE
- Test Checklist - Step-by-step checklist
- Test Matrices - Systematic coverage tracking
# Run all tests (843+ tests)
cargo test --workspace
# Run specific test types
cargo test -p ferrisscript_compiler # Unit tests (compiler)
cargo test -p ferrisscript_runtime # Unit tests (runtime)
ferris-test --all # Integration tests (.ferris scripts)
# Run specific test
cargo test test_parse_assignment
# Run with output
cargo test -- --nocaptureFerrisScript includes a headless test harness (ferris-test) for running .ferris scripts against Godot without manual intervention.
Quick Commands:
# Run all integration tests
ferris-test --all
# Run specific test
ferris-test --script godot_test/scripts/signal_test.ferris
# Filter by name
ferris-test --all --filter "node_query"
# Verbose output
ferris-test --all --verbose
# JSON format (for CI)
ferris-test --all --format json > results.jsonConfiguration: ferris-test.toml in workspace root
Test Harness Features:
- ✅ Headless Godot execution (no GUI needed)
- ✅ Test metadata parsing (TEST, CATEGORY, EXPECT, ASSERT)
- ✅ Output marker parsing ([TEST_START], [PASS], [FAIL], [TEST_END])
- ✅ Multiple output formats (console, JSON, JUnit)
- ✅ Parallel test execution with timeout handling
Documentation:
FerrisScript uses pre-commit hooks to automatically validate code before commits. These hooks ensure:
- ✅ Code is properly formatted (
cargo fmt) - ✅ No linting warnings (
cargo clippy) - ✅ All unit tests pass (
cargo test)
Installing Pre-commit Hooks:
The hooks are automatically installed when you first run cargo build. If they're not active, install them manually:
PowerShell (Windows):
.\scripts\install-git-hooks.ps1Bash (Linux/macOS):
./scripts/install-git-hooks.shWhat Happens on Commit:
When you run git commit, the pre-commit hook will automatically:
- Check formatting: Verifies
cargo fmthas been run - Run linter: Executes
cargo clippywith strict warnings - Run tests: Executes quick unit tests (not integration tests)
If any check fails, the commit is blocked and you'll see:
❌ Formatting issues detected
❌ Linting warnings found
❌ Tests failed
Running Checks Manually:
You can run the same checks locally before committing:
# Format code
cargo fmt --all
# Check linting
cargo clippy --workspace --all-targets --all-features -- -D warnings
# Run tests
cargo test --workspace
# Or use the pre-commit script directly (PowerShell)
.\.git\hooks\pre-commit
# Or use the pre-commit script directly (Bash)
./.git/hooks/pre-commitSkipping Pre-commit Hooks (Not Recommended):
In rare cases where you need to commit work-in-progress code:
git commit --no-verify -m "WIP: incomplete feature"Troubleshooting Pre-commit Hooks:
Hook not running:
# Re-install hooks
.\scripts\install-git-hooks.ps1 # Windows
./scripts/install-git-hooks.sh # Linux/macOSFormatting check fails:
# Auto-format all code
cargo fmt --allClippy warnings:
# See detailed warnings
cargo clippy --workspace --all-targets --all-features
# Fix automatically (some warnings)
cargo clippy --fix --workspace --all-targets --all-featuresTests failing:
# Run tests with output to see failures
cargo test --workspace -- --nocapture
# Run specific failing test
cargo test test_name -- --nocaptureWhy Pre-commit Hooks?
- Catches issues early: Before they reach CI or code review
- Saves time: No waiting for CI to find formatting issues
- Maintains quality: Ensures consistent code standards
- Reduces review burden: Reviewers can focus on logic, not style
We aim for high test coverage, especially for:
- Parser and lexer (edge cases, error handling)
- Type checker (all type rules, error messages)
- Runtime (execution correctness, error recovery)
FerrisScript uses two code coverage tools for different environments:
- cargo-llvm-cov - Preferred for local development (Windows, macOS, Linux)
- cargo-tarpaulin - Used in CI (Linux only, due to Windows file locking issues)
Quick Start - Running Coverage Locally:
# PowerShell (Windows)
.\scripts\coverage.ps1
# Bash (Linux/macOS)
./scripts/coverage.shThe scripts will:
- Automatically install
cargo-llvm-covif not present - Run coverage analysis across all workspace crates
- Generate both HTML and LCOV reports in
target/coverage/
Viewing Coverage Reports:
# Windows - Open HTML report
Invoke-Item target/coverage/html/index.html
# Linux
xdg-open target/coverage/html/index.html
# macOS
open target/coverage/html/index.htmlManual Coverage Commands:
# Install prerequisites (first time only)
rustup component add llvm-tools-preview
cargo install cargo-llvm-cov
# Run coverage with HTML output
cargo llvm-cov --workspace --html --output-dir target/coverage
# Run coverage with LCOV output (for external tools)
cargo llvm-cov --workspace --lcov --output-path target/coverage/lcov.info
# Run coverage for specific crate
cargo llvm-cov -p ferrisscript_compiler --htmlUnderstanding Coverage Results:
- Green lines: Covered by tests
- Red lines: Not covered by tests
- Yellow lines: Partially covered (branches)
- Coverage percentage: Shown per file and overall
Coverage Goals:
- Current baseline: Established in test coverage analysis
- Target for new code: 80%+ line coverage
- Critical paths: Parser, type checker, runtime should have high coverage
Why Two Tools?
- cargo-llvm-cov: Cross-platform, native Rust tooling, no file locking issues on Windows
- cargo-tarpaulin: More mature in CI environments, used in GitHub Actions (Linux runners)
See docs/COVERAGE_SETUP_NOTES.md for technical details on the Windows file locking issue that led to this dual-tool approach.
Before Submitting a PR:
For significant code changes:
- Run coverage locally:
.\scripts\coverage.ps1or./scripts/coverage.sh - Check that your new code is covered by tests
- Aim for 80%+ coverage on modified files
- CI will also run coverage checks using tarpaulin
Troubleshooting Coverage Issues:
- "cargo-llvm-cov not found": The script will auto-install it, or run
cargo install cargo-llvm-covmanually - Windows file locking errors: Close VS Code and rust-analyzer, or use the provided scripts which avoid these issues
- Coverage seems low: Ensure tests are actually executing your code paths; add
#[cfg(test)]module tests - CI coverage differs from local: CI uses tarpaulin (Linux), local uses llvm-cov; minor differences are normal
Currently, Godot integration testing is deferred as it requires manual setup. See FUTURE_AUTOMATION.md for plans to automate this in v0.0.3+.
New to open source? Welcome! Here's how to get started:
-
Look for beginner-friendly issues labeled:
good-first-issue- Ideal for newcomersdocumentation- Documentation improvementsintermediate- Once you're comfortable with the basics
-
Start small:
- Fix a typo in documentation
- Add an example to existing docs
- Write a test for an existing feature
-
Ask for help:
- Comment on the issue to let us know you're working on it
- Ask questions if you're stuck
- Request review from maintainers
-
Learn the workflow:
- Fork the repository
- Make your changes
- Submit a pull request
- Respond to feedback
Don't worry about making mistakes - we're here to help! Every contributor started where you are now.
- Issues: GitHub Issues for bugs and feature requests
- Discussions: Coming soon - for questions, ideas, and community chat
- Project Documentation: Check docs/ for development workflows and guides
All contributors are recognized in:
- Git commit history
- GitHub contributors page
- Future release notes (for significant contributions)
Thank you for contributing to FerrisScript! 🦀❤️
Made with 🦀 and ❤️ for the Godot community