{"id":31813987,"url":"https://github.com/levicook/odometer","last_synced_at":"2026-02-25T23:34:15.099Z","repository":{"id":298353174,"uuid":"999731352","full_name":"levicook/odometer","owner":"levicook","description":"A workspace version management tool that keeps package versions synchronized across projects","archived":false,"fork":false,"pushed_at":"2025-06-27T16:43:14.000Z","size":128,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-20T04:53:36.860Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/levicook.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-10T17:42:44.000Z","updated_at":"2025-06-27T16:43:17.000Z","dependencies_parsed_at":"2025-06-10T18:30:37.223Z","dependency_job_id":"5f91daf6-eab5-44fa-a434-ea2d7ca975e6","html_url":"https://github.com/levicook/odometer","commit_stats":null,"previous_names":["levicook/odometer"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/levicook/odometer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levicook%2Fodometer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levicook%2Fodometer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levicook%2Fodometer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levicook%2Fodometer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/levicook","download_url":"https://codeload.github.com/levicook/odometer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levicook%2Fodometer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29844878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T22:37:40.667Z","status":"ssl_error","status_checked_at":"2026-02-25T22:37:25.960Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-11T08:23:23.962Z","updated_at":"2026-02-25T23:34:15.081Z","avatar_url":"https://github.com/levicook.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Odometer 🚀\n\nA workspace version management tool that keeps package versions synchronized across projects.\n\n[![Rust](https://img.shields.io/badge/rust-1.70+-orange.svg)](https://www.rust-lang.org)\n[![Tests](https://img.shields.io/badge/tests-passing-green.svg)](https://github.com/levicook/odometer)\n\n## Overview\n\nOdometer provides intuitive commands to manage versions across project workspaces, with precise control over which packages get updated. Whether you need lockstep versioning for coordinated releases or independent versioning for different packages, odometer has you covered.\n\n**Currently supports:** Rust/Cargo workspaces and Node.js/npm workspaces  \n**Planned support:** Python/pip and other package ecosystems\n\n### Key Features\n\n- 🎯 **Precise Package Selection** - Target specific packages, all workspace members, or just the root\n- 🔄 **Flexible Version Strategies** - Independent versioning or lockstep synchronization\n- 🛡️ **Safe Defaults** - Operations target workspace root only unless explicitly specified\n- 📊 **Clear Inspection** - See current versions and validate version fields\n- 🏗️ **Workspace Inheritance Support** - Handles `version = { workspace = true }` (Cargo) and `workspace:*` (Node.js)\n- ⚡ **Fast \u0026 Reliable** - Written in Rust with comprehensive test coverage\n\n## Installation\n\n### From crates.io (Recommended)\n\n```bash\n# Install latest stable release\ncargo install odometer\n\n# Or install specific version\ncargo install odometer@0.3.1\n```\n\n### From Source (Development)\n\n```bash\ngit clone https://github.com/levicook/odometer.git\ncd odometer\ncargo install --path .\n```\n\n### Verify Installation\n\n```bash\nodo --version\n# Output: odometer 0.3.1\n```\n\n### Multiple Binary Names\n\nOdometer provides multiple binary names for convenience:\n\n```bash\nodo --help          # Short form (recommended)\nodometer --help     # Full name\ncargo odo --help    # Cargo subcommand style\ncargo odometer --help\n```\n\n## Quick Start\n\n```bash\n# See current versions\nodo show\n\n# Bump workspace root patch version\nodo roll patch\n\n# Bump all workspace members independently\nodo roll --workspace patch\n\n# Set all crates to same version (lockstep)\nodo sync 1.0.0\n\n# Validate all version fields\nodo lint\n```\n\n## Commands\n\n### `odo show` - Display Versions\n\nShows current versions for all workspace members:\n\n```bash\n$ odo show\nworkspace-root 1.0.0\nlib1 0.5.2\nlib2 0.3.1\n```\n\n### `odo roll` - Increment Versions\n\nIncrement versions with precise control:\n\n```bash\n# Bump workspace root only (safe default)\nodo roll patch              # 1.0.0 → 1.0.1\nodo roll minor              # 1.0.1 → 1.1.0\nodo roll major              # 1.1.0 → 2.0.0\n\n# Custom increments\nodo roll patch 5            # 2.0.0 → 2.0.5\nodo roll patch -2           # 2.0.5 → 2.0.3\n\n# Target all workspace members independently\nodo roll --workspace patch  # Each crate's patch version increments\n\n# Target specific packages\nodo roll --package lib1 minor\nodo roll -p lib1 -p lib2 patch\n```\n\n### `odo set` - Set Specific Versions\n\nSet exact versions for packages:\n\n```bash\n# Set workspace root to specific version\nodo set 2.1.4\n\n# Set specific packages\nodo set 1.0.0 --package lib1\nodo set 2.0.0 --workspace    # Set all workspace members\n```\n\n### `odo sync` - Lockstep Synchronization\n\nSet ALL workspace members to the same version:\n\n```bash\n# Synchronize everything to 1.0.0\nodo sync 1.0.0\n\n# After sync, all crates have identical versions\n$ odo show\nworkspace-root 1.0.0\nlib1 1.0.0\nlib2 1.0.0\n```\n\n### `odo lint` - Validate Versions\n\nCheck for missing or malformed version fields:\n\n```bash\n$ odo lint\n✅ All workspace versions are valid\n\n# Or with errors:\n$ odo lint\n❌ lib1: Invalid version 'not-a-version': unexpected character 'n' while parsing major version number\n```\n\n## Package Selection\n\nOdometer uses cargo-style package selection for precise control:\n\n| Flag              | Description           | Example                      |\n| ----------------- | --------------------- | ---------------------------- |\n| _(default)_       | Workspace root only   | `odo roll patch`             |\n| `-p, --package`   | Specific package(s)   | `odo roll patch -p lib1`     |\n| `-w, --workspace` | All workspace members | `odo roll --workspace patch` |\n\n## File and Directory Filtering\n\nOdometer respects standard ignore patterns when discovering workspace members. By default, it follows the same conventions as tools like `ripgrep` and `ag`.\n\n### Default Behavior\n\n- ✅ **Hidden files ignored** - Files/directories starting with `.` are skipped\n- ✅ **`.gitignore` respected** - Standard git ignore patterns are followed\n- ✅ **`.ignore` respected** - ripgrep/ag format ignore files are supported\n- ✅ **Global git ignore** - `~/.config/git/ignore` patterns are applied\n- ✅ **Local git exclude** - `.git/info/exclude` patterns are applied\n\n### Override Flags\n\nControl exactly what gets ignored with these flags:\n\n| Flag                 | Description                      | Example                       |\n| -------------------- | -------------------------------- | ----------------------------- |\n| `--hidden`           | Include hidden files/directories | `odo show --hidden`           |\n| `--no-ignore`        | Don't respect `.ignore` files    | `odo show --no-ignore`        |\n| `--no-ignore-git`    | Don't respect `.gitignore` files | `odo show --no-ignore-git`    |\n| `--no-ignore-global` | Don't respect global git ignore  | `odo show --no-ignore-global` |\n| `--no-ignore-all`    | Disable all ignore filtering     | `odo show --no-ignore-all`    |\n\n### Common Use Cases\n\n```bash\n# Include packages in hidden directories (e.g., .private-pkg)\nodo show --hidden\n\n# Debug: see everything odometer can find\nodo show --no-ignore-all\n\n# Ignore git patterns but respect custom .ignore files\nodo sync 1.0.0 --no-ignore-git\n\n# Work in a non-git directory with custom ignore patterns\nodo lint --no-ignore-git --no-ignore-global\n```\n\n### Ignore File Formats\n\nOdometer supports these ignore files in order of precedence:\n\n1. **`.ignore`** - ripgrep/ag format (highest precedence)\n2. **`.gitignore`** - git format\n3. **Global git ignore** - `~/.config/git/ignore` (lowest precedence)\n\nExample `.ignore` file:\n\n```\n# Ignore build artifacts\ntarget/\nnode_modules/\ndist/\n\n# Ignore temporary packages\n**/temp-*\n**/.tmp\n```\n\n### Example Workflows\n\n#### Coordinated Release Workflow\n\n```bash\n# Get everything synchronized first\nodo sync 1.0.0\nodo show                    # Verify all at 1.0.0\n\n# Now bulk operations work predictably\nodo roll --workspace minor  # All: 1.0.0 → 1.1.0\nodo lint                    # Validate everything\n```\n\n#### Independent Development Workflow\n\n```bash\n# Work on different features in different crates\nodo roll --package app minor     # App gets new feature: 1.0.0 → 1.1.0\nodo roll --package utils patch   # Utils gets bugfix: 0.5.0 → 0.5.1\nodo show                         # See current state\n\n# Bulk patch release when ready\nodo roll --workspace patch       # Each crate gets patch bump\n```\n\n#### Preparing Major Release\n\n```bash\n# Review current state\nodo show\nodo lint\n\n# Synchronize for coordinated release\nodo sync 2.0.0\nodo show                    # Confirm all at 2.0.0\n```\n\n## Workspace Support\n\nOdometer properly handles:\n\n- **Workspace roots** with `[workspace]` sections (Cargo) or `workspaces` field (Node.js)\n- **Member packages** in subdirectories\n- **Workspace inheritance**:\n  - Cargo: `version = { workspace = true }`\n  - Node.js: `\"version\": \"workspace:*\"` or `\"version\": \"workspace:~\"`\n- **Mixed scenarios** (some packages inherit, others don't)\n- **Single package projects** (no workspace)\n- **Mixed ecosystems** (Rust and Node.js packages in the same workspace)\n\n### Node.js Workspace Example\n\n```bash\n# Initialize a Node.js workspace\nmkdir my-workspace \u0026\u0026 cd my-workspace\nnpm init -y\n# Add workspaces to package.json\necho '{\"workspaces\": [\"packages/*\"]}' \u003e package.json\n\n# Create some packages\nmkdir -p packages/pkg1 packages/pkg2\ncd packages/pkg1 \u0026\u0026 npm init -y\ncd ../pkg2 \u0026\u0026 npm init -y\n\n# Now use odometer to manage versions\nodo show                    # See all package versions\nodo roll --workspace patch  # Bump all packages\nodo sync 1.0.0             # Set all to same version\n```\n\n### Rust Workspace Example\n\n```bash\n# Initialize a Rust workspace\ncargo init --lib\n# Add workspace configuration to Cargo.toml\necho '[workspace]\nmembers = [\"packages/*\"]' \u003e\u003e Cargo.toml\n\n# Create some crates\nmkdir -p packages/crate1 packages/crate2\ncd packages/crate1 \u0026\u0026 cargo init --lib\ncd ../crate2 \u0026\u0026 cargo init --lib\n\n# Now use odometer to manage versions\nodo show                    # See all crate versions\nodo roll --workspace patch  # Bump all crates\nodo sync 1.0.0             # Set all to same version\n```\n\n## Development\n\n### Setup\n\n```bash\ngit clone https://github.com/levicook/odometer.git\ncd odometer\nmake install-tools\n```\n\n### Testing\n\n```bash\nmake test              # Unit tests (fast)\nmake test-integration  # Integration tests with fixtures\nmake test-all          # Run everything\n```\n\n### Available Make Targets\n\n```bash\nmake help              # Show all available commands\nmake check             # Check code without building\nmake build             # Build project\nmake fixtures          # Generate test fixtures\nmake clean             # Clean build artifacts\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Make your changes\n4. Add tests for new functionality\n5. Run the test suite (`make test-all`)\n6. Commit your changes (`git commit -am 'Add amazing feature'`)\n7. Push to the branch (`git push origin feature/amazing-feature`)\n8. Open a Pull Request\n\n### Architecture\n\nOdometer uses a clean architecture with three main layers:\n\n- **Domain** (`src/domain.rs`) - Pure business logic for version operations\n- **IO** (`src/io/`) - File system operations (currently Cargo, designed for Node.js/Python expansion)\n- **CLI** (`src/cli.rs`) - Command-line interface and orchestration\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Acknowledgments\n\n- Built with [Rust](https://www.rust-lang.org/) 🦀\n- CLI powered by [clap](https://github.com/clap-rs/clap)\n- Version parsing via [semver](https://github.com/dtolnay/semver)\n- TOML manipulation using [toml_edit](https://github.com/ordian/toml_edit)\n\n---\n\n_Made with ❤️ for the Rust community_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevicook%2Fodometer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flevicook%2Fodometer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevicook%2Fodometer/lists"}