Skip to content

Latest commit

 

History

History
417 lines (295 loc) · 12.3 KB

File metadata and controls

417 lines (295 loc) · 12.3 KB

Contributing to the CUDly CLI

Thank you for your interest in contributing to CUDly! This document provides guidelines and instructions for contributing to the CLI component.

Code of Conduct

By participating in this project, you agree to maintain a respectful and inclusive environment. Be kind, constructive, and professional in all interactions.

How to Contribute

Reporting Bugs

  1. Search existing issues - Check if the bug has already been reported
  2. Create a detailed report including:
    • CUDly version (./cudly --help)
    • Go version (go version)
    • Operating system and architecture
    • Cloud provider and service affected
    • Steps to reproduce
    • Expected vs actual behavior
    • Relevant logs (with sensitive data removed)

Suggesting Features

  1. Search existing issues - Your idea may already be proposed
  2. Open a feature request with:
    • Clear description of the feature
    • Use case and benefits
    • Proposed implementation (if applicable)
    • Any potential drawbacks

Submitting Code

  1. Fork the repository

  2. Create a feature branch from main:

    git checkout -b feature/your-feature-name
  3. Make your changes following our coding standards

  4. Write or update tests for your changes

  5. Run the test suite to ensure everything passes

  6. Commit with clear messages following our commit conventions

  7. Push to your fork and submit a Pull Request

Development Setup

Prerequisites

  • Go 1.26.6 or later (the floor set by the go directive in go.mod)
  • Git

Getting Started

# Clone your fork
git clone https://github.com/YOUR_USERNAME/cloud-commitments-cli.git
cd cloud-commitments-cli

# Add upstream remote
git remote add upstream https://github.com/LeanerCloud/cloud-commitments-cli.git

# Install dependencies
go mod download

# Build
make build

# Run the unit tests
make test-unit

Go workspace and worktrees (gopls setup)

The repo ships a go.work that lists only this repository's own module. The shared libraries and providers are consumed at the versions pinned in go.mod, so there is nothing else to set up for a standard clone. When you are working across multiple git worktrees simultaneously, gopls needs each worktree's module added to the workspace or it flags every file in the sibling trees with BrokenImport / undefined: <Type>.

Do not edit the committed go.work for local paths -- they vary per developer and per session.

Instead, create a go.work.local next to go.work (it is gitignored): start from a copy of the committed go.work and append your active worktrees:

// go.work.local -- gitignored, developer-local
// Keep this `go` line at or above the modules' own directive, otherwise the
// workspace is rejected. Copy it from the committed go.work.
go 1.26.6

use (
    .
    ../.worktrees/cloud-commitments-cli/fix-516
    ../.worktrees/cloud-commitments-cli/feat-something
)

Then point gopls at it by setting GOWORK before launching your editor, or by symlinking it over go.work temporarily:

# Option A: set GOWORK in your shell profile or editor launcher
export GOWORK="$PWD/go.work.local"

# Option B: create go.work.local and let gopls auto-discover it
# (gopls respects GOWORK when set; otherwise it walks up for go.work)

After adding or removing a worktree, update go.work.local to match:

# Quick regeneration from git worktree list (space-safe: keeps full paths,
# adds one -use entry per worktree; skips the main checkout on line 1)
git worktree list --porcelain | sed -n 's/^worktree //p' | tail -n +2 |
  while IFS= read -r wt; do go work edit -use "$wt"; done

The committed go.work (listing only this repository's own modules) keeps go vet ./..., go test ./..., and CI clean for everyone without requiring any local setup. Note that go build ./... is not a valid way to build this repo: cmd/ is the only main package, and Go's default output name for a lone main package collides with the cmd directory itself. Use make build or go build -o cudly ./cmd instead (see "Getting Started" above).

Running Tests

# Run the unit tests
make test-unit

# The same suite, invoked directly
go test -short -race ./...

# Run tests with coverage
go test -cover ./...

# Run tests for a specific package
go test ./cmd/...

# Run tests with verbose output
go test -v ./...

# Run a specific test
go test -run TestFunctionName ./path/to/package

Test Coverage Goals

We aim to maintain the following minimum test coverage:

Package Minimum Coverage
CLI commands 60%

Coding Standards

Go Style

  • Follow the Effective Go guidelines
  • Use gofmt to format code
  • Use golint and go vet to catch issues
  • Keep functions focused and reasonably sized
  • Write clear, self-documenting code

Naming Conventions

  • Use CamelCase for exported names, camelCase for unexported
  • Use meaningful, descriptive names
  • Interfaces describing behavior should end in -er (e.g., Reader, Writer)
  • Test files: *_test.go
  • Mock implementations: prefix with mock

Documentation

  • All exported functions, types, and packages must have doc comments
  • Use complete sentences starting with the name being documented
  • Include usage examples for complex functionality
  • Keep comments up to date with code changes

Error Handling

  • Always handle errors explicitly
  • Wrap errors with context using fmt.Errorf("context: %w", err)
  • Use custom error types for domain-specific errors
  • Never ignore errors silently

Testing

  • Write table-driven tests where appropriate
  • Use interfaces and dependency injection for testability
  • Mock external dependencies (AWS/Azure/GCP SDKs)
  • Test both success and error paths
  • Include edge cases in test coverage

Project Structure

cloud-commitments-cli/
├── cmd/                      # CLI entry point, commands, and helpers
├── docs/                     # CLI documentation
├── scripts/                  # Repository hook and helper scripts
├── go.mod                    # Pins the shared modules from cloud-commitments-go
└── Makefile                  # build, test, vet, and lint targets

Adding a New Service

Service clients live in github.com/LeanerCloud/cloud-commitments-go. In this repository:

  1. Add the service's command and flags in cmd/
  2. Reuse the service client from the pinned provider module
  3. Register the service in the CLI's service selection
  4. Write comprehensive tests
  5. Update docs/

Adding a New Cloud Provider

Provider implementations live in github.com/LeanerCloud/cloud-commitments-go (providers/aws, providers/azure, providers/gcp). Open the change there. This repository consumes providers at the versions pinned in go.mod, so bumping that pin is the only change needed here.

Commit Guidelines

Commit Message Format

type(scope): brief description

Longer description if needed. Explain what and why,
not how (the code shows how).

Fixes #123

Types

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation only
  • style: Formatting, missing semicolons, etc.
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Performance improvement
  • test: Adding or updating tests
  • chore: Build process, dependencies, etc.

Examples

feat(aws): add MemoryDB reserved node support

Implements purchase and recommendation fetching for
Amazon MemoryDB reserved nodes.

Fixes #42
fix(azure): handle subscription pagination correctly

The previous implementation missed subscriptions after
the first page. Now properly iterates all pages.

Pull Request Process

  1. Update documentation for any user-facing changes
  2. Add or update tests for your changes
  3. Ensure all tests pass before submitting
  4. Fill out the PR template completely
  5. Request review from maintainers
  6. Address feedback promptly and constructively

PR Checklist

  • Code follows project style guidelines
  • Tests added/updated and passing
  • Documentation updated
  • Commit messages follow conventions
  • No sensitive data in code or commits
  • Changes are backwards compatible (or breaking changes documented)

Known Issues Sweep

This repository has no known_issues/ directory. Deferred work found while reviewing a change here belongs in this repository's GitHub issues. The cross-component sweep, which covers known_issues/ in the platform repository, is documented there.

Entry format

Each file must begin with a # Known Issues: <topic> heading followed by an audit-status line:

> **Audit status (<YYYY-MM-DD>):** `<N> needs triage · <M> resolved`

Subsequent sections use ## SEVERITY: Short title (e.g. ## MEDIUM: ...) and include at minimum: Files (affected paths), Description, Why deferred, and Status.

When to ADD an entry

  • Tech debt or a follow-up bug is discovered during a PR review but is explicitly out of scope for that PR.
  • A test is marked flaky and a root-cause fix is deferred.
  • A deliberate deferral is made (e.g. "fix after the current refactor lands").

Create a new file known_issues/<NN>_<slug>.md (sequential number, lowercase slug) and open a corresponding GitHub issue so it can be tracked and closed.

When to REMOVE (archive) an entry

An entry is stale when its corresponding GitHub issue is closed OR when the entry has had no recurrence for more than 6 months and no open issue references it. Do not delete stale files; move them to known_issues/resolved/ so the rationale is preserved for future readers.

Who runs the sweep and when

Any contributor working in a file covered by a known_issues/ doc should check whether that doc's referenced issue is still open; archive it if not.

A dedicated sweep over the whole directory should happen:

  • At the start of each sprint (or monthly if sprints are not used).
  • After any PR that explicitly closes multiple issues.
  • When a new contributor is onboarding and doing a codebase walkthrough.

To perform a sweep:

# List docs referencing a specific issue number
grep -rl "#<issue-number>" known_issues/

# Cross-check all referenced issues in bulk
grep -h "closes #\|Fixes #\|#[0-9]\+" known_issues/*.md \
  | grep -oE '#[0-9]+' | sort -u \
  | xargs -I{} gh issue view {} --json state,number,title --jq '[.number,.state,.title]'

Move resolved docs to known_issues/resolved/:

git mv known_issues/<file>.md known_issues/resolved/

Include the archive in the same PR that closes the underlying issue, or in a dedicated chore(docs): archive resolved known_issues commit.

Security

Reporting Vulnerabilities

Do not report security vulnerabilities through public issues.

Instead, please email security concerns to the maintainers directly. Include:

  • Description of the vulnerability
  • Steps to reproduce
  • Potential impact
  • Suggested fix (if any)

Security Best Practices

  • Never commit credentials or secrets
  • Use environment variables for sensitive configuration
  • Validate all external input
  • Follow least-privilege principles
  • Keep dependencies updated

Areas for Contribution

We welcome contributions in these areas:

High Priority

  • Additional AWS services (Lambda, DynamoDB, etc.)
  • Azure service implementations
  • GCP service implementations
  • Improved error messages and user experience

Medium Priority

  • Enhanced reporting and analytics
  • Terraform/CloudFormation integration
  • Web UI dashboard
  • Performance optimizations

Documentation

  • Usage tutorials and guides
  • Architecture documentation
  • API documentation
  • Translation to other languages

Getting Help

  • Issues: Open a GitHub issue for bugs or features
  • Discussions: Use GitHub Discussions for questions
  • Documentation: Check the README and code comments

License

By contributing to CUDly, you agree that your contributions will be licensed under the Open Software License 3.0 (OSL-3.0).

This means:

  • Your contributions can be used commercially
  • Derivative works must also be OSL-3.0 licensed
  • You grant a patent license for your contributions
  • Attribution must be maintained

Acknowledgments

Thank you to all contributors who help make CUDly better! Your time and expertise are greatly appreciated.