Skip to content

Contributing

Thank you for considering a contribution to FORGE. Whether you are reporting a bug, proposing a feature, improving documentation, or submitting code, this guide walks you through the process from setup to pull request.

Getting Started

Prerequisites

Before contributing, make sure you have the following installed:

ToolVersionPurpose
Rust1.75+Compile FORGE CLI and core
Cargo(bundled with Rust)Build system and package manager
Git2.30+Version control
Node.js18+Build and preview documentation
Docker24+Run integration tests
PostgreSQL15+Database for integration tests

Clone and Build

bash
# Clone the repository
git clone https://github.com/forge/forge.git
cd forge

# Build the project
cargo build

# Run the test suite
cargo test

# Run with debug output
RUST_LOG=debug cargo run -- new --name=testapp

TIP

The first build downloads and compiles all dependencies, which may take a few minutes. Subsequent builds are incremental and much faster.

Verify Your Setup

Run the following to confirm everything works:

bash
# Build in release mode
cargo build --release

# Run clippy for lint checks
cargo clippy -- -D warnings

# Check formatting
cargo fmt --check

Project Structure

FORGE is organized as a Cargo workspace with two crates:

forge/
+-- Cargo.toml              # Workspace root
+-- forge-cli/               # CLI binary crate
|   +-- src/
|   |   +-- main.rs          # Entry point and command routing
|   |   +-- commands/        # CLI command implementations
|   |   +-- utils/           # CLI utilities (output, prompts)
|   +-- Cargo.toml
|
+-- forge-core/              # Library crate (business logic)
|   +-- src/
|   |   +-- lib.rs           # Library root
|   |   +-- config/          # Project configuration parsing
|   |   +-- generator/       # Code generation engine
|   |   +-- template/        # Template loading and rendering
|   |   +-- providers/       # Provider contract definitions
|   |   +-- validators/      # Input and config validation
|   +-- Cargo.toml
|
+-- templates/               # Tera template files
|   +-- backends/
|   |   +-- rust/            # Rust + Axum backend templates
|   +-- frontends/
|   |   +-- nextjs/          # Next.js frontend templates
|   +-- infra/               # Docker, Caddy, config templates
|
+-- docs/                    # VitePress documentation (this site)
+-- tests/                   # Integration tests
+-- README.md

Key Crates

CrateTypeDescription
forge-cliBinaryParses CLI arguments, dispatches commands, manages terminal output
forge-coreLibraryConfig parsing, template rendering, file generation, validation

Development Workflow

1. Create a Branch

Always work on a feature branch, never directly on main:

bash
# Create a branch from main
git checkout main
git pull origin main
git checkout -b feat/my-feature

Use these branch name prefixes:

PrefixPurposeExample
feat/New featurefeat/add-nuxtjs-support
fix/Bug fixfix/template-rendering-error
docs/Documentation onlydocs/update-api-contracts
refactor/Code restructuringrefactor/simplify-generator
test/Test additionstest/add-config-validation

2. Make Changes

Write your code following the style guidelines below. Keep commits focused -- each commit should represent a single logical change.

3. Test Your Changes

bash
# Run all tests
cargo test

# Run a specific test
cargo test test_name

# Run tests with output
cargo test -- --nocapture

# Run clippy
cargo clippy -- -D warnings

# Check formatting
cargo fmt --check

4. Submit a Pull Request

bash
# Push your branch
git push -u origin feat/my-feature

Then open a pull request on GitHub. Include:

  • A clear title describing what the PR does
  • A description of the changes and why they are needed
  • Steps to test the changes manually (if applicable)
  • Screenshots for UI changes

WARNING

All pull requests must pass CI checks before merging. This includes tests, clippy, and formatting.

Template Development

Templates are the heart of FORGE. Every file in a generated project is rendered from a Tera template.

Template File Structure

Template files use the .tera extension and live under templates/:

templates/backends/rust/api/src/handlers/auth.rs.tera

This template generates the file:

{output}/apps/api/src/handlers/auth.rs

Template Variables

Templates have access to the project configuration via context variables:

{{ project_name }}          - Application name
{{ project_name_pascal }}   - PascalCase name
{{ auth_method }}           - "email", "mobile", or "otp"
{{ default_language }}      - Default language code
{{ languages }}             - Array of language objects
{{ features }}              - Enabled features

Conditional Generation

Use Tera conditionals to include or exclude code based on configuration:

rust
// In a .tera template file
{% if auth_method == "otp" %}
pub async fn send_otp(/* ... */) -> Result<Json<Value>, AppError> {
    // OTP-specific logic
}
{% endif %}

Testing Templates

Test that your templates render correctly by generating a project and inspecting the output:

bash
# Build FORGE
cargo build

# Generate a test project
./target/debug/forge new --name=test-output

# Inspect the generated files
cat test-output/apps/api/src/handlers/auth.rs

TIP

When modifying templates, always test with multiple configuration combinations (different auth methods, different languages, features enabled/disabled) to ensure all code paths render correctly.

Template Style Guidelines

  • Maintain the output file's language conventions (e.g., Rust idioms for .rs.tera files)
  • Use {% raw %}...{% endraw %} blocks around content that contains Tera-like syntax (e.g., JSX curly braces)
  • Keep template logic minimal -- prefer generating clean, readable code over clever template tricks
  • Add comments in the generated output explaining non-obvious patterns

Code Style

Rust Code

FORGE follows standard Rust conventions enforced by rustfmt and clippy:

bash
# Format all code
cargo fmt

# Run linter with warnings as errors
cargo clippy -- -D warnings

Key conventions:

  • Use snake_case for functions, variables, and module names
  • Use PascalCase for types, traits, and enums
  • Prefer Result<T, E> over panics
  • Use thiserror for error types
  • Document public APIs with /// doc comments
  • Keep functions under 50 lines when possible

Commit Messages

Follow the Conventional Commits specification:

feat: add Nuxt.js frontend generator
fix: resolve template rendering for RTL languages
docs: update API contracts reference
refactor: simplify config parsing logic
test: add unit tests for permission validation
chore: update dependencies

Commit message rules:

  • Use the imperative mood (add, not added or adds)
  • Keep the subject line under 72 characters
  • Add a body for complex changes explaining the "why"
  • Reference issue numbers when applicable: fix: handle empty slug (#42)

Testing

FORGE uses three levels of testing:

Unit Tests

Test individual functions and modules in isolation.

rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validate_project_name() {
        assert!(validate_name("my-app").is_ok());
        assert!(validate_name("My App").is_err());
        assert!(validate_name("123app").is_err());
    }
}

Integration Tests

Test the full generation pipeline from config to output files.

rust
// tests/integration/generate_test.rs

#[test]
fn test_generate_rust_backend() {
    let config = ProjectConfig {
        name: "test-app".into(),
        backend: Backend::Rust,
        auth_method: AuthMethod::Email,
        ..Default::default()
    };

    let output = tempdir().unwrap();
    generate_project(&config, output.path()).unwrap();

    // Verify expected files exist
    assert!(output.path().join("apps/api/src/main.rs").exists());
    assert!(output.path().join("apps/api/Cargo.toml").exists());
}

Template Rendering Tests

Verify that templates produce valid output for all configuration combinations.

rust
#[test]
fn test_auth_handler_email_method() {
    let context = create_context(AuthMethod::Email);
    let output = render_template("backends/rust/api/src/handlers/auth.rs.tera", &context);

    assert!(output.contains("pub async fn login"));
    assert!(!output.contains("pub async fn send_otp"));
}

#[test]
fn test_auth_handler_otp_method() {
    let context = create_context(AuthMethod::Otp);
    let output = render_template("backends/rust/api/src/handlers/auth.rs.tera", &context);

    assert!(output.contains("pub async fn login"));
    assert!(output.contains("pub async fn send_otp"));
}

Documentation Contributions

The documentation site is built with VitePress and lives in the docs/ directory.

Running the Docs Locally

bash
cd docs

# Install dependencies
npm install

# Start development server
npm run dev

# Build for production
npm run build

Writing Documentation

  • Use VitePress features: ::: tip, ::: warning, ::: danger callouts
  • Use code groups (::: code-group) for multi-language or multi-format examples
  • Add tables for structured data instead of long prose
  • Include working code examples whenever possible
  • Link to related pages using relative paths: [Authentication](/backend/authentication)

File Naming

  • Use lowercase with hyphens: api-contracts.md, not ApiContracts.md
  • Place files in the appropriate directory matching the sidebar structure
  • Update .vitepress/config.mts if adding a new page to the sidebar

Issue Reporting

Bug Reports

When filing a bug report, include:

  1. FORGE version (forge --version)
  2. Operating system and version
  3. Rust version (rustc --version)
  4. Steps to reproduce the issue
  5. Expected behavior vs. actual behavior
  6. Error output (full terminal output, not screenshots)
  7. Configuration (forge.yaml contents, if relevant)

TIP

Use the GitHub issue template when available. It guides you through providing all the necessary information.

Feature Requests

Feature requests should include:

  1. Problem statement -- what are you trying to accomplish?
  2. Proposed solution -- how should FORGE handle it?
  3. Alternatives considered -- other approaches you thought of
  4. Context -- who would benefit from this feature?

Code of Conduct

All contributors are expected to follow the project's Code of Conduct:

  • Be respectful -- Treat everyone with respect. Disagree constructively.
  • Be constructive -- Focus on what is best for the project and community.
  • Be collaborative -- Share knowledge and help others.
  • Be patient -- Not everyone has the same experience level.
  • No harassment -- Harassment of any kind will not be tolerated.

Violations should be reported to the project maintainers via email. All reports are kept confidential.

Getting Help

If you need help with your contribution:

See Also

Released under the MIT License.