Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

scoop is a centralized Python virtual environment manager powered by uv.

One scoop, endless envs — pyenv-style workflow with uv’s blazing speed.

What is scoop?

Think of it like running an ice cream parlor:

  • The Freezer (~/.scoop/) keeps all your flavors fresh
  • Flavors are your virtualenvs — mix once, serve anywhere
  • One scoop is all you need to get the right env
The Old WayThe scoop Way
.venv scattered across projects~/.scoop/virtualenvs/ centralized
Manual source .venv/bin/activateAuto-activate on directory entry
pyenv-virtualenv is slowuv-powered, 100x+ faster
Which Python? Which venv? Chaos.scoop doctor checks everything

Quick Example

# Install Python
scoop install 3.12

# Create a virtualenv
scoop create myproject 3.12

# Use it (auto-activates!)
scoop use myproject
(myproject) $ pip install requests

# Check what's available
scoop list

Features

  • Fast — Powered by uv, virtualenv creation is nearly instant
  • Centralized — All environments live in ~/.scoop/virtualenvs/
  • Auto-activation — Enter a directory, environment activates automatically
  • Shell integration — Works with bash and zsh
  • pyenv compatible — Reads .python-version files
  • Health checksscoop doctor diagnoses your setup

Getting Started

Ready to scoop? Head to the Installation guide to get started.

Installation

Prerequisites

DependencyVersionInstall Command
uvLatestcurl -LsSf https://astral.sh/uv/install.sh | sh
Rust1.85+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Install via Cargo

cargo install scoop-uv

The binary is installed to ~/.cargo/bin/scoop.

Verify Installation

scoop --version
# scoop 0.2.10

Troubleshooting

scoop: command not found

Ensure ~/.cargo/bin is in your PATH:

# Add to ~/.zshrc or ~/.bashrc
export PATH="$HOME/.cargo/bin:$PATH"

Then restart your terminal or run:

source ~/.zshrc  # or ~/.bashrc

uv not found

scoop requires uv to be installed and available in PATH. Verify:

uv --version

If not installed, run:

curl -LsSf https://astral.sh/uv/install.sh | sh

Next Steps

After installation, set up Shell Integration to enable auto-activation and tab completion.

Quick Start

This guide walks you through the basic scoop workflow.

1. Set Up Shell Integration

Zsh (macOS default):

echo 'eval "$(scoop init zsh)"' >> ~/.zshrc
source ~/.zshrc

Bash:

echo 'eval "$(scoop init bash)"' >> ~/.bashrc
source ~/.bashrc

2. Install Python

# Install latest Python
scoop install 3.12

# Verify installation
scoop list --pythons

3. Create a Virtual Environment

scoop create myproject 3.12

This creates a virtual environment at ~/.scoop/virtualenvs/myproject/.

4. Use the Environment

cd ~/projects/myproject
scoop use myproject

This:

  1. Creates .scoop-version file in the current directory
  2. Activates the environment (prompt shows (myproject))

5. Work With Your Environment

(myproject) $ pip install requests
(myproject) $ python -c "import requests; print(requests.__version__)"

6. Auto-Activation

Once configured, entering a directory with .scoop-version automatically activates the environment:

cd ~/projects/myproject
# (myproject) appears in prompt automatically

Common Commands

TaskCommand
List environmentsscoop list
List Python versionsscoop list --pythons
Show environment infoscoop info myproject
Remove environmentscoop remove myproject
Check installationscoop doctor

IDE Integration

Create a .venv symlink for IDE compatibility:

scoop use myproject --link

This creates .venv pointing to the scoop environment, recognized by VS Code, PyCharm, etc.

Next Steps

Shell Integration

scoop uses a shell wrapper pattern (like pyenv) where the CLI outputs shell code that gets evaluated by the shell.

How It Works

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ User runs   │ --> │ CLI outputs │ --> │ Shell evals │
│ scoop use   │     │ export ...  │     │ the output  │
└─────────────┘     └─────────────┘     └─────────────┘

The scoop shell function wraps the CLI binary:

scoop() {
    case "$1" in
        use)
            command scoop "$@"
            eval "$(command scoop activate "$name")"
            ;;
        activate|deactivate|shell)
            eval "$(command scoop "$@")"
            ;;
        *)
            command scoop "$@"
            ;;
    esac
}

Setup

Zsh

echo 'eval "$(scoop init zsh)"' >> ~/.zshrc
source ~/.zshrc

Bash

echo 'eval "$(scoop init bash)"' >> ~/.bashrc
source ~/.bashrc

Fish

echo 'eval (scoop init fish)' >> ~/.config/fish/config.fish
source ~/.config/fish/config.fish

Auto-Activation

When enabled, scoop automatically activates environments based on version files.

Zsh: Uses chpwd hook (runs on directory change)

autoload -Uz add-zsh-hook
add-zsh-hook chpwd _scoop_hook

Bash: Uses PROMPT_COMMAND

PROMPT_COMMAND="_scoop_hook;$PROMPT_COMMAND"

Fish: Uses --on-variable PWD event handler

function _scoop_hook --on-variable PWD
    # Check for version file and activate/deactivate
end

The hook checks for version files and activates/deactivates accordingly.

Version Resolution Priority

scoop checks these sources in order (first match wins):

PrioritySourceSet by
1SCOOP_VERSION env varscoop shell
2.scoop-version filescoop use
3.python-version filepyenv compatibility
4~/.scoop/version filescoop use --global

The “system” Value

When any source contains the value system, scoop deactivates the current virtual environment and uses the system Python.

scoop use system          # Write "system" to .scoop-version
scoop shell system        # Set SCOOP_VERSION=system (this terminal only)

Environment Variables

VariableDescriptionDefault
SCOOP_HOMEBase directory~/.scoop
SCOOP_VERSIONOverride version (highest priority)(unset)
SCOOP_NO_AUTODisable auto-activation(unset)
SCOOP_ACTIVECurrently active environment(set by scoop)
SCOOP_RESOLVE_MAX_DEPTHLimit parent directory traversal(unlimited)

Disable Auto-Activation

export SCOOP_NO_AUTO=1

Custom Home Directory

export SCOOP_HOME=/custom/path

Network Filesystem Optimization

For slow network filesystems (NFS, SSHFS), limit directory traversal depth:

# Only check current directory and up to 3 parents
export SCOOP_RESOLVE_MAX_DEPTH=3

# Only check current directory (fastest)
export SCOOP_RESOLVE_MAX_DEPTH=0

Using with pyenv

Add scoop after pyenv in your shell config:

# ~/.zshrc
eval "$(pyenv init -)"       # 1. pyenv first
eval "$(scoop init zsh)"     # 2. scoop second (takes precedence)

Tab Completion

Shell integration includes completion for:

  • Commands and subcommands
  • Environment names
  • Python versions
  • Command options

Completion is automatically enabled by scoop init.

Supported Shells

ShellStatus
ZshFull support (auto-activation, completion)
BashFull support (auto-activation, completion)
FishFull support (auto-activation, completion)
PowerShellPlanned (P3)

Frequently Asked Questions

What’s the difference between scoop and pyenv?

While both tools help you manage Python, they focus on different parts of the workflow:

pyenv is primarily a version manager. It focuses on:

  • Installing multiple versions of the Python interpreter (e.g., 3.9.0, 3.12.1)
  • Switching between them globally or per folder

scoop is an environment and workflow manager powered by uv. It focuses on:

  • Creating and managing isolated virtual environments
  • Fast project-specific environment workflows

Summary: You might use pyenv to install Python 3.11 on your machine, but you use scoop to actually build and run your application within a lightning-fast virtual environment using that Python version.

Can I use scoop with conda environments?

Not directly. They serve different purposes and operate independently:

conda is a package and environment manager. It handles:

  • Its own binaries and non-Python dependencies
  • Heavy data science libraries (MKL, CUDA, cuDNN, etc.)

scoop is a lightweight environment manager powered by uv. It:

  • Leverages your existing Python installations
  • Creates fast, portable virtual environments

When to use what: For heavy data science requiring non-Python libraries → conda. For almost everything else → scoop (significantly faster and more portable).

How do I uninstall scoop completely?

To remove scoop from your system:

1. Delete the data folder

rm -rf ~/.scoop

2. Remove the shell hook

Edit your shell config file and remove the scoop init line:

ShellConfig FileLine to Remove
Bash~/.bashrceval "$(scoop init bash)"
Zsh~/.zshrceval "$(scoop init zsh)"
Fish~/.config/fish/config.fishscoop init fish | source

3. (Optional) Remove config

rm -rf ~/.config/scoop

4. Restart your terminal

Does scoop work on Windows?

Yes! Since scoop is built on top of uv, it has excellent cross-platform support.

You can use it via:

  • PowerShell (Recommended)
  • Command Prompt (cmd.exe)
  • Git Bash or WSL

Note: Make sure to follow the Windows-specific shell integration steps in the Installation guide.

Command Reference

Complete reference for all scoop commands.

Commands Overview

CommandAliasesDescription
scoop listlsList virtualenvs or Python versions
scoop create-Create virtualenv
scoop use-Set + activate environment
scoop removerm, deleteRemove virtualenv
scoop install-Install Python version
scoop uninstall-Uninstall Python version
scoop doctor-Diagnose installation
scoop info-Show virtualenv details
scoop migrate-Migrate from pyenv/conda/venvwrapper
scoop lang-Get/set display language
scoop init-Shell init script
scoop completions-Completion script

Global Options

Available for all commands:

OptionDescription
-q, --quietSuppress all output
--no-colorDisable colored output
-h, --helpShow help message
-V, --versionShow version

Environment Variables

VariableDescriptionDefault
SCOOP_HOMEBase directory for scoop~/.scoop
SCOOP_NO_AUTODisable auto-activation(unset)
SCOOP_LANGDisplay language (en, ko)System locale
NO_COLORDisable colored output(unset)

Directory Layout

LocationPurpose
~/.scoop/virtualenvs/Virtual environments storage
~/.scoop/versionGlobal default environment
.scoop-versionLocal environment preference
.python-versionpyenv compatibility (fallback)
.venvSymlink to active environment (with --link)

list

List all virtual environments or installed Python versions.

Aliases: ls

Usage

scoop list [options]

Options

OptionDescription
--pythonsShow Python versions instead of virtualenvs
--bareOutput names only (for scripting)
--jsonOutput as JSON

Examples

scoop list                  # List all virtualenvs
scoop list --pythons        # List installed Python versions
scoop list --bare           # Names only, one per line
scoop list --json           # JSON output

create

Create a new virtual environment.

Usage

scoop create <name> [python-version]

Arguments

ArgumentRequiredDefaultDescription
nameYes-Name for the new virtualenv
python-versionNo3 (latest)Python version (e.g., 3.12, 3.11.8)

Options

OptionDescription
--force, -fOverwrite existing virtualenv

Examples

scoop create myproject 3.12      # Create with Python 3.12
scoop create webapp              # Create with latest Python
scoop create myenv 3.11 --force  # Overwrite if exists

use

Set a virtual environment for the current directory and activate it.

Usage

scoop use <name> [options]
scoop use system [options]
scoop use --unset [options]

Arguments

ArgumentRequiredDescription
nameNoName of the virtualenv, or system for system Python

Options

OptionDescription
--unsetRemove version file (local or global)
--global, -gSet as global default
--linkCreate .venv symlink for IDE compatibility
--no-linkDo not create .venv symlink (default)

Behavior

  • Creates .scoop-version file in current directory
  • Immediately activates the environment (if shell hook installed)
  • With --global: writes to ~/.scoop/version
  • With --link: creates .venv -> ~/.scoop/virtualenvs/<name>

Special Value: system

Using system as the name tells scoop to use the system Python:

scoop use system           # Use system Python in this directory
scoop use system --global  # Use system Python as global default

This writes the literal string system to the version file, which the shell hook interprets as “deactivate any virtual environment.”

The --unset Flag

Removes the version file entirely:

scoop use --unset           # Delete .scoop-version in current directory
scoop use --unset --global  # Delete ~/.scoop/version

After unsetting, scoop falls back to the next priority level in version resolution.

Examples

# Use a virtual environment in this directory
scoop use myproject

# Also create .venv symlink (for IDE support)
scoop use myproject --link

# Set global default environment
scoop use myproject --global

# Use system Python in this directory
scoop use system

# Use system Python globally
scoop use system --global

# Remove local version setting
scoop use --unset

# Remove global version setting
scoop use --unset --global

Version File Format

The .scoop-version file contains a single line with either:

  • An environment name (e.g., myproject)
  • The literal string system
$ cat .scoop-version
myproject

remove

Remove a virtual environment.

Aliases: rm, delete

Usage

scoop remove <name> [options]

Arguments

ArgumentRequiredDescription
nameYesName of the virtualenv to remove

Options

OptionDescription
--force, -fSkip confirmation prompt

Examples

scoop remove myproject           # Remove with confirmation
scoop remove myproject --force   # Remove without asking
scoop rm old-env -f              # Using alias

install

Install a Python version.

Usage

scoop install [version] [options]

Arguments

ArgumentRequiredDefaultDescription
versionNolatestPython version (e.g., 3.12, 3.11.8)

Options

OptionDescription
--latestInstall latest stable Python (default)
--stableInstall oldest fully-supported Python (3.10)

Version Resolution

  • No argument or --latest: installs latest Python 3.x
  • --stable: installs Python 3.10 (oldest with active security support)
  • 3.12: installs latest 3.12.x patch
  • 3.12.3: installs exact version

Examples

scoop install                    # Install latest
scoop install --latest           # Same as above
scoop install --stable           # Install Python 3.10
scoop install 3.12               # Install latest 3.12.x
scoop install 3.12.3             # Install exact 3.12.3

Note: Python versions are managed by uv.

uninstall

Remove an installed Python version.

Usage

scoop uninstall <version>

Arguments

ArgumentRequiredDescription
versionYesPython version to remove

Examples

scoop uninstall 3.12             # Remove Python 3.12
scoop uninstall 3.11.8           # Remove specific version

doctor

Check scoop installation health and diagnose issues.

Usage

scoop doctor [options]

Options

OptionDescription
-v, --verboseShow more details (can repeat: -vv)
--jsonOutput diagnostics as JSON
--fixAuto-fix issues where possible

Checks Performed

  • uv installation and version
  • Shell integration status
  • Environment integrity
  • Path configuration
  • Version file validity

Examples

scoop doctor                     # Quick health check
scoop doctor -v                  # Verbose diagnostics
scoop doctor --fix               # Fix what can be fixed
scoop doctor --json              # JSON output for scripting

info

Show detailed information about a virtual environment.

Usage

scoop info <name>

Arguments

ArgumentRequiredDescription
nameYesName of the virtualenv

Examples

scoop info myproject             # Show details for myproject

migrate

Migrate virtual environments from other tools (pyenv-virtualenv, virtualenvwrapper, conda).

Usage

# List migratable environments
scoop migrate list

# Migrate a single environment
scoop migrate @<name>

# Migrate all environments
scoop migrate --all

Subcommands

SubcommandDescription
listList environments available for migration
@<name>Migrate a single environment by name
--allMigrate all discovered environments

Supported Sources

SourceDetection
pyenv-virtualenv~/.pyenv/versions/ (non-system virtualenvs)
virtualenvwrapper$WORKON_HOME or ~/.virtualenvs/
condaconda info --envs

Options

OptionDescription
--jsonOutput as JSON
--quietSuppress output
--no-colorDisable colored output

Examples

List Migratable Environments

$ scoop migrate list
📦 Migratable Environments

  pyenv-virtualenv:
    • myproject (Python 3.12.0)
    • webapp (Python 3.11.8)

  conda:
    • ml-env (Python 3.10.4)

Migrate Single Environment

$ scoop migrate @myproject
✓ Migrated 'myproject' from pyenv-virtualenv
  Source: ~/.pyenv/versions/myproject
  Target: ~/.scoop/virtualenvs/myproject

Migrate All

$ scoop migrate --all
✓ Migrated 3 environments
  • myproject (pyenv-virtualenv)
  • webapp (pyenv-virtualenv)
  • ml-env (conda)

JSON Output

$ scoop migrate list --json
{
  "status": "success",
  "data": {
    "environments": [
      {
        "name": "myproject",
        "source": "pyenv",
        "python_version": "3.12.0"
      }
    ]
  }
}

Migration Process

  1. Discovery: Scans configured source paths for virtual environments
  2. Extraction: Identifies Python version and installed packages
  3. Recreation: Creates new scoop environment with same Python version
  4. Package Install: Reinstalls packages using uv pip install
  5. Cleanup: Original environment is preserved (not deleted)

Notes

  • Original environments are not deleted during migration
  • Package versions are preserved where possible
  • Migration creates fresh environments using uv for improved performance

lang

Get or set the display language for scoop CLI messages.

Usage

# Show current language
scoop lang

# Set language
scoop lang <code>

# List supported languages
scoop lang --list

# Reset to system default
scoop lang --reset

Arguments

ArgumentDescription
<code>Language code to set (e.g., en, ko)

Options

OptionDescription
--listList all supported languages
--resetReset to system default language
--jsonOutput as JSON

Supported Languages

CodeLanguage
enEnglish (default)
ko한국어 (Korean)

Language Detection Priority

  1. SCOOP_LANG environment variable
  2. ~/.config/scoop/config.toml setting
  3. System locale (via sys-locale)
  4. Default: en

Examples

Show Current Language

$ scoop lang
Current language: en (English)

Set Korean

$ scoop lang ko
✓ Language set to Korean (한국어)

List Languages

$ scoop lang --list
Supported languages:
  en - English
  ko - 한국어 (Korean)

Reset to System Default

$ scoop lang --reset
✓ Language reset to system default

JSON Output

$ scoop lang --json
{
  "status": "success",
  "data": {
    "current": "ko",
    "name": "한국어",
    "source": "config"
  }
}

Configuration

Language preference is stored in:

# ~/.config/scoop/config.toml
lang = "ko"

Environment Variable Override

# Temporarily use English regardless of config
SCOOP_LANG=en scoop list

# Set for current session
export SCOOP_LANG=ko

Notes

  • CLI help text (--help) remains in English (industry standard)
  • JSON output keys remain in English (machine-readable)
  • Error messages, success messages, and prompts are translated

init

Output shell initialization script.

Usage

scoop init <shell>

Arguments

ArgumentRequiredDescription
shellYesShell type: bash, zsh, fish, powershell

Setup

Add to your shell configuration:

# Bash (~/.bashrc)
eval "$(scoop init bash)"

# Zsh (~/.zshrc)
eval "$(scoop init zsh)"
# Fish (~/.config/fish/config.fish)
eval (scoop init fish)

Features Enabled

  • Auto-activation when entering directories with .scoop-version
  • Tab completion for commands, environments, and options
  • Wrapper function for activate/deactivate/use

Examples

scoop init bash                  # Output bash init script
scoop init zsh                   # Output zsh init script
scoop init fish                  # Output fish init script

shell

Set shell-specific environment (current shell session only).

Unlike scoop use which writes to a file, scoop shell sets the SCOOP_VERSION environment variable for the current shell session only.

Usage

eval "$(scoop shell <name>)"    # Bash/Zsh
scoop shell <name> | source     # Fish

Note: If you have shell integration set up (scoop init), the eval is automatic:

scoop shell myenv    # Works directly

Arguments

ArgumentRequiredDescription
nameNoEnvironment name or system

Options

OptionDescription
--unsetClear shell-specific environment
--shell <SHELL>Target shell type (auto-detected if not specified)

Behavior

  • Sets SCOOP_VERSION environment variable
  • If name is an environment: also outputs activation script
  • If name is system: also outputs deactivation script
  • --unset: outputs unset SCOOP_VERSION

Priority

SCOOP_VERSION has the highest priority in version resolution:

1. SCOOP_VERSION env var    <- scoop shell (highest)
2. .scoop-version file      <- scoop use
3. .python-version file     <- pyenv compatibility
4. ~/.scoop/version         <- scoop use --global

This means scoop shell overrides any file-based settings until:

  • You run scoop shell --unset
  • You close the terminal

Examples

# Use a specific environment in this terminal
scoop shell myproject

# Use system Python in this terminal
scoop shell system

# Clear the shell setting (return to file-based resolution)
scoop shell --unset

# Explicit shell type
scoop shell --shell fish myenv

Use Cases

Temporary Testing

# Currently using myproject
scoop shell testenv        # Switch to testenv temporarily
python test.py             # Test something
scoop shell myproject      # Switch back

Override Project Settings

cd ~/project               # Has .scoop-version = projectenv
scoop shell system         # Use system Python anyway
python --version           # System Python
scoop shell --unset        # Back to projectenv

completions

Generate shell completion script.

Usage

scoop completions <shell>

Arguments

ArgumentRequiredDescription
shellYesShell type: bash, zsh, fish, powershell

Examples

scoop completions bash           # Output bash completions
scoop completions zsh            # Output zsh completions
scoop completions fish           # Output fish completions

Tip: Usually you don’t need this separately - scoop init includes completions.

Contributing

Guide for contributing to scoop development.

Prerequisites

  • Rust 1.85+ (Edition 2024)
  • uv - Python package manager (install)
  • prek - Pre-commit hooks (install)

Setup

# Clone the repository
git clone https://github.com/ai-screams/scoop-uv.git
cd scoop-uv

# Install prek (Rust-native pre-commit alternative)
uv tool install prek
# or: cargo install prek

# Install git hooks
prek install

# Build
cargo build

# Run tests
cargo test

Project Structure

src/
├── main.rs              # Entry point
├── lib.rs               # Library root
├── error.rs             # Error types (ScoopError)
├── paths.rs             # Path utilities
├── validate.rs          # Name/version validation

├── uv/                  # uv client wrapper
│   ├── mod.rs
│   └── client.rs

├── core/                # Business logic
│   ├── mod.rs
│   ├── virtualenv.rs    # VirtualenvService
│   ├── version.rs       # VersionService
│   ├── metadata.rs      # Metadata structs
│   └── doctor.rs        # Health diagnostics

├── cli/                 # CLI layer
│   ├── mod.rs           # Cli struct, Commands enum
│   └── commands/        # Command handlers
│       ├── mod.rs
│       ├── list.rs
│       ├── create.rs
│       ├── use_env.rs
│       ├── remove.rs
│       ├── install.rs
│       ├── doctor.rs
│       └── ...

├── shell/               # Shell integration
│   ├── mod.rs
│   ├── bash.rs
│   └── zsh.rs

└── output/              # Output formatting
    ├── mod.rs
    └── spinner.rs

docs/                    # Public documentation
.docs/                   # Internal technical docs
tests/                   # Integration tests

Common Commands

Build and Run

cargo build              # Debug build
cargo build --release    # Release build (optimized)
cargo run -- --help      # Show help
cargo run -- list        # Run commands
cargo run -- doctor      # Check setup health

Quick Quality Check

# All checks at once (recommended before commit)
cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings && cargo test

For detailed guides, see:

Architecture

Key Services

VirtualenvService (src/core/virtualenv.rs)

  • Manages virtualenvs in ~/.scoop/virtualenvs/
  • Wraps uv commands for venv creation

VersionService (src/core/version.rs)

  • Manages .scoop-version files
  • Resolves current directory to active environment

Doctor (src/core/doctor.rs)

  • Health diagnostics for scoop setup
  • Checks uv, shell integration, paths, environments

UvClient (src/uv/client.rs)

  • Wrapper for uv CLI commands
  • Python version management

Shell Integration

Shell scripts are embedded in Rust code:

  • src/shell/bash.rs - Bash init script
  • src/shell/zsh.rs - Zsh init script

Key components:

  1. Wrapper function - Intercepts use/activate/deactivate
  2. Hook function - Auto-activation on directory change
  3. Completion function - Tab completion

Adding a New Command

  1. Define command in src/cli/mod.rs:
#![allow(unused)]
fn main() {
#[derive(Subcommand)]
pub enum Commands {
    // ...
    MyCommand {
        #[arg(short, long)]
        option: bool,
    },
}
}
  1. Create handler in src/cli/commands/my_command.rs:
#![allow(unused)]
fn main() {
pub fn execute(output: &Output, option: bool) -> Result<()> {
    // Implementation
    Ok(())
}
}
  1. Export in src/cli/commands/mod.rs

  2. Wire up in src/main.rs

  3. Add shell completion in src/shell/{bash,zsh}.rs

Testing

Unit Tests

Located within source files:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_something() {
        // ...
    }
}
}

Integration Tests

Located in tests/:

#![allow(unused)]
fn main() {
use assert_cmd::Command;

#[test]
fn test_cli_command() {
    Command::cargo_bin("scoop")
        .unwrap()
        .args(["list"])
        .assert()
        .success();
}
}

Release Process

Releases are automated via release-plz:

  1. Create PR with changes
  2. Merge to main
  3. release-plz creates release PR
  4. Merge release PR to publish to crates.io

Internal Documentation

See .docs/ for internal technical references:

  • TECHNICAL_REFERENCE.md - Implementation details
  • SHELL_GOTCHAS.md - Shell integration pitfalls
  • IMPLEMENTATION_PLAN.md - Development roadmap
  • brand/brand.md - Brand guidelines

Code Style

  • Follow Rust conventions
  • Run cargo fmt before committing
  • Keep functions small and focused
  • Document public APIs with /// comments
  • Use thiserror for error types
  • Korean error messages with solutions (per CLAUDE.md)

Translation Guide

This document provides guidelines for contributing translations to scoop.

Current Status

For the latest translation status, see:


Contribution Process

Step 1: Fork and Clone

git clone https://github.com/YOUR_USERNAME/scoop-uv.git
cd scoop-uv

Step 2: Add Translations

Edit locales/app.yml and add your language to every key:

create.success:
  en: "Created '%{name}' environment"
  ko: "'%{name}' 환경 생성됨"
  pt-BR: "Ambiente '%{name}' criado"
  { lang }: "Your translation here"  # Add your language code and translation

Important:

  • Add translations to ALL ~106 keys
  • Keep placeholder syntax exactly: %{name}, %{version}, etc.
  • Preserve special characters: , quotes, backticks

Step 3: Register Language

Edit src/i18n.rs and add your language to SUPPORTED_LANGS:

#![allow(unused)]
fn main() {
pub const SUPPORTED_LANGS: &[(&str, &str)] = &[
    ("en", "English"),
    ("ko", "한국어"),
    ("pt-BR", "Português (Brasil)"),
    ("{lang}", "Your Language Name"),  // Add your language
];
}

Language Code Format:

  • Use BCP 47 format
  • Simple languages: ja, fr, es, de, it
  • Regional variants: pt-BR, zh-CN, zh-TW, es-MX

Step 4: Test Locally

# Build and test
cargo build
cargo test

# Test your language (replace {lang} with your language code)
SCOOP_LANG={lang} ./target/debug/scoop --help
SCOOP_LANG={lang} ./target/debug/scoop lang

Step 5: Create Pull Request

Required files in PR:

  • locales/app.yml - All 106 keys translated
  • src/i18n.rs - Language registered in SUPPORTED_LANGS

PR Title Format:

docs(i18n): add {Language Name} translation

Style Guidelines

Philosophy: Your Language, Your Style

We trust translators. You know your language and community best.

  • Word choice is yours — Pick terms that feel natural to native speakers
  • Creativity welcome — Witty expressions are fine if they’re clear and widely understood
  • Casual over formal — scoop is a friendly CLI tool, not enterprise software

General Principles

  1. Concise: CLI messages should be short and clear
  2. Natural: Use natural phrasing, not word-for-word translation
  3. Casual: Friendly, approachable tone — like talking to a colleague
  4. Clear: Wit is great, but clarity comes first

Tone Examples

# Too formal (avoid)
"The environment has been successfully created."

# Too robotic (avoid)
"Environment creation: complete."

# Good - casual and clear
"Created 'myenv' — ready to go!"
"'myenv' is ready"

Message Types

TypeEnglish ExampleGuidance
Progress“Installing…”Use progressive/ongoing form
Success“Created ‘myenv’”Completion — feel free to add flair
Error“Can’t find ‘myenv’”Clear and actionable
Hint“→ Create: scoop create…”Helpful, not lecturing

Translator’s Discretion

These decisions are up to you:

  • Vocabulary: Choose words that resonate with your community
  • Idioms: Use local expressions if they fit naturally
  • Humor: Light wit is welcome (e.g., ice cream puns if appropriate)
  • Formality level: Lean casual, but match your culture’s CLI norms

Only requirement: The meaning must be clear to users.

Technical Terms

For technical vocabulary:

  1. Check your community — What do Python developers in your language use?
  2. Consistency — Pick one term and stick with it throughout
  3. Loanwords OK — If your community uses English terms (e.g., “install”), that’s fine

Tip: Study existing translations in locales/app.yml for reference, but don’t feel bound by them.


Glossary

Do NOT Translate

These terms should remain in English in all languages:

TermReason
scoopBrand name
uvTool name
pyenvTool name
condaTool name
virtualenvTechnical term
virtualenvwrapperTool name
PythonLanguage name
shellTechnical term (bash, zsh, fish)
JSONFormat name
PATHEnvironment variable
pipTool name

Commands - Never Translate

All commands and code examples must stay in English:

# WRONG - Command translated
hint: "→ Create: {translated_command} myenv 3.12"

# CORRECT - Only description translated
hint: "→ {translated_word}: scoop create myenv 3.12"

Common Terms to Translate

These are core concepts you’ll need to translate. Reference existing translations for consistency:

EnglishWhat to look for
environmentYour language’s term for “environment”
createCommon verb for “make/create”
remove/deleteCommon verb for “delete/remove”
installStandard software installation term
uninstallStandard software removal term
activateTerm for “enable/turn on”
deactivateTerm for “disable/turn off”
migrateIT term for migration (often kept as loanword)
versionYour language’s term for “version”
pathYour language’s term for file path
errorYour language’s term for “error”
successYour language’s term for “success”

Tip: Check how these terms are translated in existing translations for reference.

Ice Cream Metaphor (README only)

scoop uses ice cream metaphors in documentation:

TermMeaningGuidance
scoopThe toolAlways keep as “scoop”
flavorvirtualenvTranslate if the metaphor works in your language
freezer~/.scoop/ directoryTranslate if the metaphor works

Note: The metaphor is mainly in README.md, not in CLI messages (locales/app.yml).


File Structure

locales/app.yml

# Categories in order:
# 1. lang.*        - Language command messages
# 2. create.*      - Create command messages
# 3. remove.*      - Remove command messages
# 4. list.*        - List command messages
# 5. use.*         - Use command messages
# 6. install.*     - Install command messages
# 7. uninstall.*   - Uninstall command messages
# 8. migrate.*     - Migrate command messages
# 9. error.*       - Error messages
# 10. suggestion.* - Suggestion/hint messages

src/i18n.rs

#![allow(unused)]
fn main() {
// Language detection priority:
// 1. SCOOP_LANG environment variable
// 2. Config file (~/.config/scoop/config.toml)
// 3. System locale
// 4. Default: "en"

pub const SUPPORTED_LANGS: &[(&str, &str)] = &[
    ("en", "English"),
    // ... existing languages
    // Add new languages here
];
}

Common Mistakes

1. Missing SUPPORTED_LANGS Registration

Symptom: Translation exists but scoop lang {code} doesn’t work

Fix: Add language to src/i18n.rs SUPPORTED_LANGS

2. Broken Placeholders

# WRONG - Missing placeholder
error: "Cannot find environment"

# CORRECT - Placeholder preserved
error: "Cannot find '%{name}' environment"

3. Translating Commands

# WRONG - Command translated
hint: "→ List: {translated} list"

# CORRECT - Only label translated
hint: "→ {Translated Label}: scoop list"

4. Inconsistent Key Coverage

All languages must have ALL keys. Missing keys fall back to English.


Testing Checklist

Before submitting PR:

  • All 106 keys translated
  • All placeholders preserved (%{name}, %{version}, etc.)
  • Language registered in SUPPORTED_LANGS
  • cargo build succeeds
  • cargo test passes
  • SCOOP_LANG={code} scoop lang shows your language
  • Messages display correctly in terminal

Questions?

  • Open an issue: GitHub Issues
  • See existing translations for reference: locales/app.yml

Architecture

scoop is built in Rust using a modular architecture.

Module Structure

src/
├── cli/              # Command-line interface
│   ├── mod.rs        # Cli struct, Commands enum, ShellType
│   └── commands/     # Individual command handlers
├── core/             # Domain logic
│   ├── doctor.rs     # Health diagnostics
│   ├── metadata.rs   # Virtualenv metadata (JSON)
│   ├── version.rs    # Version file resolution
│   └── virtualenv.rs # Virtualenv entity
├── shell/            # Shell integration
│   ├── mod.rs        # Shell module exports
│   ├── bash.rs       # Bash init script
│   ├── zsh.rs        # Zsh init script
│   └── fish.rs       # Fish init script
├── output/           # Terminal UI and JSON output
├── uv/               # uv CLI wrapper
├── error.rs          # ScoopError enum
├── paths.rs          # Path utilities
└── validate.rs       # Input validation

Key Components

CLI Layer (cli/)

  • Uses clap for argument parsing
  • Cli struct defines global options
  • Commands enum defines subcommands
  • Each command has an execute function in commands/

Core Layer (core/)

ModulePurpose
doctorHealth check system with Check trait
metadataJSON metadata for virtualenvs
versionVersion file discovery and parsing
virtualenvVirtualenv entity and operations

Shell Layer (shell/)

Generates shell scripts for integration:

  • init_script() - Returns shell initialization code
  • Wrapper function for scoop command
  • Auto-activation hooks
  • Tab completion definitions

Output Layer (output/)

Handles terminal output formatting:

UV Layer (uv/)

Wraps the uv CLI for Python/virtualenv operations:

  • Python installation
  • Virtualenv creation
  • Version listing

Design Patterns

Shell Eval Pattern

The CLI outputs shell code to stdout, which the shell evaluates:

# User runs
scoop activate myenv

# CLI outputs
export VIRTUAL_ENV="/Users/x/.scoop/virtualenvs/myenv"
export PATH="/Users/x/.scoop/virtualenvs/myenv/bin:$PATH"
export SCOOP_ACTIVE="myenv"

# Shell wrapper evaluates this output
eval "$(command scoop activate myenv)"

This pattern is used by pyenv, rbenv, and other version managers.

Error Handling

Uses thiserror for error types:

#![allow(unused)]
fn main() {
#[derive(Debug, Error)]
pub enum ScoopError {
    #[error("가상환경 '{name}'을(를) 찾을 수 없습니다")]
    VirtualenvNotFound { name: String },
    // ...
}
}

Path Management

Centralizes path logic in paths.rs:

  • scoop_home() - Returns SCOOP_HOME or ~/.scoop
  • virtualenvs_dir() - Returns virtualenvs directory
  • version_file() - Returns global version file path

Data Flow

User Command
    │
    ▼
CLI Parser (clap)
    │
    ▼
Command Handler (cli/commands/)
    │
    ├──► Core Logic (core/)
    │        │
    │        ▼
    │    UV Wrapper (uv/)
    │
    ▼
Output Formatter (output/)
    │
    ▼
stdout/stderr

Dependencies

CratePurpose
clapArgument parsing
serdeSerialization
thiserrorError types
owo-colorsTerminal colors
indicatifProgress bars
dirsHome directory
whichBinary lookup

Testing

Comprehensive guide for testing scoop.

Quick Reference

cargo test                          # Run all tests
cargo test json                     # Run tests containing "json"
cargo test -- --nocapture           # Show println! output
cargo clippy -- -D warnings         # Lint check

Test Structure

tests/
└── cli.rs                    # CLI integration tests

src/
├── error.rs                  # Unit tests for error types
├── validate.rs               # Unit tests for validation
├── paths.rs                  # Unit tests for path utilities
├── output/
│   └── json.rs               # Unit tests for JSON output
├── core/
│   ├── virtualenv.rs         # Unit tests for virtualenv service
│   ├── version.rs            # Unit tests for version service
│   ├── metadata.rs           # Unit tests for metadata
│   └── doctor.rs             # Unit tests for doctor
├── shell/
│   ├── bash.rs               # Shell script tests
│   └── zsh.rs                # Shell script tests
└── uv/
    └── client.rs             # Unit tests for uv client

Running Tests

All Tests

# Run all tests
cargo test

# Run with all features enabled
cargo test --all-features

# Run in release mode (faster execution)
cargo test --release

Filtered Tests

# By name pattern
cargo test json                     # Tests containing "json"
cargo test error                    # Tests containing "error"
cargo test virtualenv               # Tests containing "virtualenv"

# By module path
cargo test output::json             # Tests in output/json.rs
cargo test error::tests             # Tests in error.rs
cargo test core::version            # Tests in core/version.rs
cargo test cli::commands            # Tests in cli/commands/

# Single test
cargo test test_json_response_success_creates_correct_status

Test Output

# Show stdout/stderr (println!, dbg!, etc.)
cargo test -- --nocapture

# Show test names as they run
cargo test -- --nocapture --test-threads=1

# Only show failed tests
cargo test -- --quiet

Debugging

# Run single-threaded (easier to debug)
cargo test -- --test-threads=1

# Run ignored tests
cargo test -- --ignored

# Run specific test with output
cargo test test_name -- --nocapture --test-threads=1

Test Categories

Unit Tests (239 tests)

Located within source files using #[cfg(test)]:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_something() {
        assert_eq!(1 + 1, 2);
    }
}
}

Key test modules:

ModuleTestsCoverage
error::tests54Error types, codes, suggestions
output::json::tests35JSON serialization, edge cases
validate::tests30Name/version validation
core::version::tests18Version file resolution
core::virtualenv::tests12Virtualenv service
paths::tests16Path utilities
shell::*::tests14Shell scripts (shellcheck)

Integration Tests (41 tests)

Located in tests/cli.rs:

# Run only integration tests
cargo test --test cli

Categories:

  • Error cases - Invalid inputs, missing arguments
  • Output format - Help, version, JSON output
  • Command behavior - list, create, use, remove

Some tests are marked #[ignore] because they require uv installed:

# Run ignored tests (requires uv)
cargo test -- --ignored

Doc Tests (6 tests)

Examples in documentation comments:

#![allow(unused)]
fn main() {
/// Validates environment name.
///
/// # Examples
///
/// ```
/// use scoop_uv::validate::is_valid_env_name;
/// assert!(is_valid_env_name("myenv"));
/// assert!(!is_valid_env_name("123bad"));
/// ```
pub fn is_valid_env_name(name: &str) -> bool { ... }
}
# Run only doc tests
cargo test --doc

Property Tests

Using proptest for randomized testing:

#![allow(unused)]
fn main() {
use proptest::prelude::*;

proptest! {
    #[test]
    fn prop_valid_names_accepted(name in "[a-zA-Z][a-zA-Z0-9_-]{0,49}") {
        assert!(is_valid_env_name(&name));
    }
}
}

Located in src/validate.rs.

Writing Tests

Unit Test Template

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    // ========================================
    // Test Group Name
    // ========================================

    #[test]
    fn test_function_name_expected_behavior() {
        // Arrange
        let input = "test input";

        // Act
        let result = function_under_test(input);

        // Assert
        assert_eq!(result, expected_value);
    }

    #[test]
    fn test_function_name_edge_case() {
        let result = function_under_test("");
        assert!(result.is_err());
    }
}
}

Integration Test Template

#![allow(unused)]
fn main() {
// tests/cli.rs
use assert_cmd::Command;
use predicates::prelude::*;

#[test]
fn test_command_success() {
    Command::cargo_bin("scoop")
        .unwrap()
        .args(["list"])
        .assert()
        .success()
        .stdout(predicate::str::contains("expected output"));
}

#[test]
fn test_command_failure() {
    Command::cargo_bin("scoop")
        .unwrap()
        .args(["use", "nonexistent"])
        .assert()
        .failure()
        .stderr(predicate::str::contains("not found"));
}
}

JSON Output Testing

#![allow(unused)]
fn main() {
#[test]
fn test_json_serialization() {
    let data = MyData { field: "value".into() };
    let json = serde_json::to_string(&data).unwrap();

    // Check JSON structure
    let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed["field"], "value");
}

#[test]
fn test_optional_field_omitted() {
    let data = MyData { optional: None, .. };
    let json = serde_json::to_string(&data).unwrap();

    // skip_serializing_if = "Option::is_none"
    assert!(!json.contains("optional"));
}
}

Test Utilities

Located in src/test_utils.rs:

#![allow(unused)]
fn main() {
use scoop_uv::test_utils::*;

#[test]
fn test_with_temp_environment() {
    with_temp_scoop_home(|temp_dir| {
        // SCOOP_HOME is set to temp_dir
        // Cleanup happens automatically
    });
}

#[test]
fn test_with_mock_venv() {
    with_temp_scoop_home(|temp_dir| {
        create_mock_venv("myenv", Some("3.12"));
        // Virtual environment created at temp_dir/virtualenvs/myenv
    });
}
}

Coverage

Using cargo-tarpaulin

# Install
cargo install cargo-tarpaulin

# Run with HTML report
cargo tarpaulin --out Html --output-dir coverage

# Run with specific target
cargo tarpaulin --out Html --output-dir coverage --packages scoop-uv

# View report
open coverage/tarpaulin-report.html

Using cargo-llvm-cov

# Install
cargo install cargo-llvm-cov

# Run with HTML report
cargo llvm-cov --html

# View report
open target/llvm-cov/html/index.html

CI/CD Testing

Tests run automatically on:

  • Every push to any branch
  • Every pull request

GitHub Actions workflow (.github/workflows/ci.yml):

- name: Run tests
  run: cargo test --all-features

- name: Run clippy
  run: cargo clippy --all-targets -- -D warnings

Troubleshooting

Test Hangs

# Run single-threaded to identify hanging test
cargo test -- --test-threads=1

Flaky Tests

# Run specific test multiple times
for i in {1..10}; do cargo test test_name || break; done

Environment Issues

# Clear test artifacts
cargo clean

# Rebuild and test
cargo test

Shell Tests Fail

ShellCheck must be installed for shell script tests:

# macOS
brew install shellcheck

# Linux
apt install shellcheck

Best Practices

  1. Test naming: test_<function>_<scenario>_<expected>
  2. Arrange-Act-Assert: Clear test structure
  3. One assertion per test: When practical
  4. Test edge cases: Empty, unicode, special chars, boundaries
  5. No test interdependencies: Each test should be isolated
  6. Fast tests: Mock external dependencies

Code Quality

Comprehensive guide for maintaining code quality in scoop.

Quick Reference

# Format code
cargo fmt

# Lint check
cargo clippy --all-targets --all-features -- -D warnings

# All checks (pre-commit style)
cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings && cargo test

# Pre-commit hooks
prek run --all-files

Formatting (rustfmt)

Configuration

Located in rustfmt.toml:

edition = "2024"
max_width = 100
use_small_heuristics = "Default"

Commands

# Auto-format all files
cargo fmt

# Check formatting (CI mode, no changes)
cargo fmt --check

# Format specific file
rustfmt src/main.rs

# Show diff instead of applying
cargo fmt -- --check --diff

IDE Integration

VS Code (rust-analyzer):

{
  "[rust]": {
    "editor.formatOnSave": true
  }
}

JetBrains (RustRover/CLion):

  • Settings → Languages → Rust → Rustfmt → Run on save

Linting (Clippy)

Basic Usage

# Standard lint check
cargo clippy

# Treat warnings as errors (CI mode)
cargo clippy -- -D warnings

# All targets (including tests, examples)
cargo clippy --all-targets

# All features enabled
cargo clippy --all-features

# Full CI check
cargo clippy --all-targets --all-features -- -D warnings

Lint Categories

# Enable specific lint category
cargo clippy -- -W clippy::pedantic

# Deny specific lint
cargo clippy -- -D clippy::unwrap_used

# Allow specific lint
cargo clippy -- -A clippy::too_many_arguments

Common Lints

LintSeverityDescription
clippy::unwrap_usedWarnUse ? or expect() instead
clippy::panicWarnAvoid panic in library code
clippy::todoWarnRemove before release
clippy::dbg_macroWarnRemove debug macros
clippy::print_stdoutWarnUse logging instead

Fixing Lints

# Auto-fix where possible
cargo clippy --fix

# Allow fixes that change behavior
cargo clippy --fix --allow-dirty --allow-staged

Suppressing Lints

#![allow(unused)]
fn main() {
// Single line
#[allow(clippy::too_many_arguments)]
fn complex_function(...) {}

// Entire module
#![allow(clippy::module_inception)]

// With explanation
#[allow(clippy::unwrap_used)] // Safe: validated in parse()
fn get_value() {}
}

Pre-commit Hooks (prek)

Setup

# Install prek
cargo install prek
# or
uv tool install prek

# Install hooks in repository
prek install

Configuration

Located in .pre-commit-config.yaml:

repos:
  - repo: local
    hooks:
      - id: cargo-fmt
        name: cargo fmt
        entry: cargo fmt --all --
        language: system
        types: [ rust ]
        pass_filenames: false

      - id: cargo-clippy
        name: cargo clippy
        entry: cargo clippy --all-targets --all-features -- -D warnings
        language: system
        types: [ rust ]
        pass_filenames: false

      - id: cargo-check
        name: cargo check
        entry: cargo check --all-targets
        language: system
        types: [ rust ]
        pass_filenames: false

Usage

# Run all hooks on staged files
prek run

# Run all hooks on all files
prek run --all-files

# Run specific hook
prek run cargo-fmt
prek run cargo-clippy

# Run multiple specific hooks
prek run cargo-fmt cargo-clippy

# Skip hooks (emergency only!)
git commit --no-verify

Available Hooks

HookDescriptionWhen
cargo-fmtCode formattingPre-commit
cargo-clippyLintingPre-commit
cargo-checkType checkingPre-commit
trailing-whitespaceRemove trailing spacesPre-commit
end-of-file-fixerEnsure newline at EOFPre-commit
check-tomlValidate TOML filesPre-commit
check-yamlValidate YAML filesPre-commit

CI Pipeline

GitHub Actions

Located in .github/workflows/ci.yml:

name: CI

on: [ push, pull_request ]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust
        uses: dtolnay/rust-action@stable
        with:
          components: rustfmt, clippy

      - name: Format check
        run: cargo fmt --all -- --check

      - name: Clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

      - name: Test
        run: cargo test --all-features

Local CI Simulation

# Run exactly what CI runs
cargo fmt --check && \
cargo clippy --all-targets --all-features -- -D warnings && \
cargo test --all-features

Code Style Guidelines

Naming Conventions

ItemConventionExample
Modulessnake_caseversion_file
Functionssnake_caseget_version()
TypesPascalCaseVirtualenvService
ConstantsSCREAMING_SNAKEMAX_NAME_LENGTH
Lifetimesshort lowercase'a, 'src

Documentation

/// Creates a new virtual environment.
///
/// # Arguments
///
/// * `name` - Environment name (must be valid)
/// * `python` - Python version (e.g., "3.12")
///
/// # Returns
///
/// Path to the created environment.
///
/// # Errors
///
/// Returns [`ScoopError::InvalidEnvName`] if name is invalid.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let path = create_env("myenv", "3.12")?;
/// # Ok(())
/// # }
/// ```
pub fn create_env(name: &str, python: &str) -> Result<PathBuf> {
    // ...
}

Error Handling

#![allow(unused)]
fn main() {
// Good: Use ? operator
fn process() -> Result<()> {
    let file = File::open(path)?;
    let data = read_data(&file)?;
    Ok(())
}

// Good: Contextual errors
fn process() -> Result<()> {
    let file = File::open(path)
        .map_err(|e| ScoopError::Io(e))?;
    Ok(())
}

// Avoid: unwrap() in library code
fn bad() {
    let file = File::open(path).unwrap(); // Bad
}

// OK: expect() with explanation
fn acceptable() {
    let home = dirs::home_dir()
        .expect("home directory must exist");
}
}

Import Organization

#![allow(unused)]
fn main() {
// 1. Standard library
use std::collections::HashMap;
use std::path::PathBuf;

// 2. External crates
use clap::Parser;
use serde::Serialize;
use thiserror::Error;

// 3. Local modules
use crate::error::Result;
use crate::paths;
}

Security Considerations

Dependency Auditing

# Install cargo-audit
cargo install cargo-audit

# Run audit
cargo audit

# Fix vulnerabilities
cargo audit fix

MSRV (Minimum Supported Rust Version)

  • Current MSRV: 1.85
  • Defined in Cargo.toml:
    [package]
    rust-version = "1.85"
    

Unsafe Code

  • Avoid unsafe unless absolutely necessary
  • Document safety invariants
  • Use #![forbid(unsafe_code)] in library crates

Performance

Profiling

# Build with debug info for release
cargo build --release

# Use flamegraph
cargo install flamegraph
cargo flamegraph --bin scoop -- list

Benchmarks

# Run benchmarks (if defined)
cargo bench

# Using criterion
cargo bench --bench my_benchmark

Continuous Improvement

Regular Checks

# Weekly dependency update check
cargo outdated

# Security audit
cargo audit

# MSRV check
cargo msrv verify

Upgrade Dependencies

# Update Cargo.lock
cargo update

# Upgrade to latest compatible versions
cargo upgrade  # requires cargo-edit

Troubleshooting

Clippy False Positives

#![allow(unused)]
fn main() {
// Silence with explanation
#[allow(clippy::needless_return)]
fn explicit_return() -> i32 {
    return 42; // Intentional for readability
}
}

Format Conflicts

#![allow(unused)]
fn main() {
// Skip formatting for specific block
#[rustfmt::skip]
const MATRIX: [[i32; 3]; 3] = [
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1],
];
}

CI vs Local Differences

# Ensure same toolchain as CI
rustup update stable
rustup default stable

# Check Rust version
rustc --version

Summary Checklist

Before committing:

  • cargo fmt - Code formatted
  • cargo clippy -- -D warnings - No lint warnings
  • cargo test - All tests pass
  • cargo doc - Documentation builds
  • No todo!() or dbg!() left in code
  • Public APIs documented
  • Error messages are helpful