guides

Contributing

How to contribute to Cloud Cost Guard

6 min read
Updated 2026-02-24

Contributing to OpsCurb

Thank you for your interest in contributing to OpsCurb! This document provides guidelines for contributing to our open-source components and community.


๐Ÿค Ways to Contribute

1. Report Bugs

Found a bug? Help us fix it!

Where: GitHub Issues

Include:

  • Clear description of the bug
  • Steps to reproduce
  • Expected vs. actual behavior
  • Screenshots (if applicable)
  • Environment (OS, browser, AWS region)
  • Error messages or logs

Template:

**Bug Description**
A clear description of what the bug is.

**Steps to Reproduce**
1. Go to '...'
2. Click on '...'
3. See error

**Expected Behavior**
What you expected to happen.

**Actual Behavior**
What actually happened.

**Environment**
- OS: [e.g., macOS 13.0]
- Browser: [e.g., Chrome 120]
- OpsCurb Version: [e.g., 1.2.0]

2. Suggest Features

Have an idea? We'd love to hear it!

Where: Feature Requests

Include:

  • Feature description
  • Use case / problem it solves
  • Proposed solution
  • Alternatives considered
  • Priority (nice-to-have vs. critical)

3. Improve Documentation

Documentation improvements are always welcome!

What to Improve:

  • Fix typos or grammar
  • Clarify confusing sections
  • Add examples
  • Update outdated information
  • Translate to other languages

How:

  1. Fork the repository
  2. Make your changes
  3. Submit a pull request

4. Contribute Code

We welcome code contributions!

What We Accept:

  • Bug fixes
  • New scanner types
  • Performance improvements
  • Test coverage improvements
  • Refactoring

What We Don't Accept (without prior discussion):

  • Major architectural changes
  • New dependencies
  • Breaking changes

๐Ÿ› ๏ธ Development Setup

Prerequisites

  • Python 3.11+
  • Node.js 18+
  • PostgreSQL 15+
  • AWS Account (for testing)
  • Git

Local Setup

1. Clone the repository:

git clone https://github.com/opscurb/cloud-cost-guard.git
cd cloud-cost-guard

2. Set up backend:

cd api
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your configuration

3. Set up frontend:

cd frontend
npm install
cp .env.local.example .env.local
# Edit .env.local with your configuration

4. Set up database:

# Create database
createdb opscurb

# Run migrations
cd database
psql opscurb < migrations/001_initial_schema.sql

5. Run locally:

# Terminal 1: Backend
cd api
uvicorn main:app --reload

# Terminal 2: Frontend
cd frontend
npm run dev

6. Access:


๐Ÿ“ Coding Standards

Python (Backend)

Style: PEP 8

Tools:

  • black for formatting
  • flake8 for linting
  • mypy for type checking

Run:

black .
flake8 .
mypy .

Example:

from typing import List, Optional
from pydantic import BaseModel


class Finding(BaseModel):
    """Represents a cost optimization finding."""
    
    id: str
    resource_id: str
    severity: str
    estimated_monthly_cost: float
    
    def get_annual_cost(self) -> float:
        """Calculate annual cost."""
        return self.estimated_monthly_cost * 12

TypeScript (Frontend)

Style: Airbnb TypeScript Style Guide

Tools:

  • prettier for formatting
  • eslint for linting

Run:

npm run format
npm run lint

Example:

interface Finding {
  id: string;
  resourceId: string;
  severity: 'high' | 'medium' | 'low';
  estimatedMonthlyCost: number;
}

export function getAnnualCost(finding: Finding): number {
  return finding.estimatedMonthlyCost * 12;
}

๐Ÿงช Testing

Backend Tests

Framework: pytest

Run:

cd api
pytest
pytest --cov  # With coverage

Example:

def test_calculate_ebs_cost():
    """Test EBS cost calculation."""
    volume = {
        "VolumeId": "vol-123",
        "Size": 100,
        "VolumeType": "gp3"
    }
    cost = calculate_ebs_cost(volume, "us-east-1")
    assert cost == 8.0  # $0.08/GB * 100GB

Frontend Tests

Framework: Jest + React Testing Library

Run:

cd frontend
npm test
npm test -- --coverage  # With coverage

Example:

import { render, screen } from '@testing-library/react';
import FindingCard from './FindingCard';

test('renders finding card', () => {
  const finding = {
    id: '1',
    resourceId: 'vol-123',
    severity: 'high',
    estimatedMonthlyCost: 80
  };
  
  render(<FindingCard finding={finding} />);
  expect(screen.getByText('vol-123')).toBeInTheDocument();
});

๐Ÿ”€ Pull Request Process

Before Submitting

  1. Create an issue (if one doesn't exist)
  2. Fork the repository
  3. Create a branch: git checkout -b feature/your-feature-name
  4. Make your changes
  5. Write tests
  6. Run tests: Ensure all tests pass
  7. Run linters: Fix any linting errors
  8. Update documentation: If needed
  9. Commit: Use clear commit messages

Commit Messages

Format:

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

<body>

<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting)
  • refactor: Code refactoring
  • test: Test changes
  • chore: Build/tooling changes

Example:

feat(scanners): add CloudWatch Logs scanner

Add new scanner to detect unused CloudWatch Log Groups.
Identifies log groups with no recent activity and estimates
cost savings from deletion.

Closes #123

Submitting PR

  1. Push to your fork: git push origin feature/your-feature-name
  2. Create pull request on GitHub
  3. Fill out PR template
  4. Wait for review
  5. Address feedback
  6. Get approval
  7. Merge!

PR Template

## Description
Brief description of changes.

## Related Issue
Closes #123

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Tests added/updated
- [ ] All tests passing
- [ ] Manual testing completed

## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] No new warnings

๐ŸŽฏ Adding a New Scanner

Want to add a new AWS scanner? Here's how:

1. Create scanner file:

# scanners/cloudwatch_logs_scanner.py
from typing import List, Dict
from .base_scanner import BaseScanner

class CloudWatchLogsScanner(BaseScanner):
    """Scanner for unused CloudWatch Log Groups."""
    
    def scan(self, session) -> List[Dict]:
        """Scan for unused log groups."""
        client = session.client('logs')
        findings = []
        
        # Implementation here
        
        return findings

2. Add tests:

# tests/test_cloudwatch_logs_scanner.py
def test_cloudwatch_logs_scanner():
    """Test CloudWatch Logs scanner."""
    # Test implementation

3. Register scanner:

# scanners/__init__.py
from .cloudwatch_logs_scanner import CloudWatchLogsScanner

SCANNERS = [
    # ... existing scanners
    CloudWatchLogsScanner,
]

4. Update documentation:

  • Add to README.md
  • Add to API_DOCUMENTATION.md

๐Ÿ“œ Code of Conduct

Our Pledge

We pledge to make participation in our community a harassment-free experience for everyone.

Our Standards

Positive behavior:

  • Using welcoming language
  • Being respectful
  • Accepting constructive criticism
  • Focusing on what's best for the community

Unacceptable behavior:

  • Harassment or discrimination
  • Trolling or insulting comments
  • Personal or political attacks
  • Publishing others' private information

Enforcement

Violations may result in:

  1. Warning
  2. Temporary ban
  3. Permanent ban

Report: conduct@opscurb.com


๐Ÿ“ž Getting Help

Questions: community@opscurb.com
Discord: Join our Discord
Forum: community.opscurb.com


๐Ÿ† Recognition

Contributors are recognized in:

  • README.md contributors section
  • Release notes
  • Annual contributor awards

Top contributors may receive:

  • Free Pro plan
  • Exclusive swag
  • Early access to features

Thank you for contributing! ๐ŸŽ‰


Last Updated: 2026-02-24
Version: 1.0