reqsync


Namereqsync JSON
Version 0.1.2 PyPI version JSON
download
home_pageNone
SummarySynchronize requirements.txt to match installed versions, safely and atomically.
upload_time2025-08-20 06:34:36
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords agent ai-tool automation ci-cd cli-tool dependencies dependency-management devops packaging pip requirements venv
VCS
bugtrack_url
requirements typer packaging portalocker tomli typing-extensions rich
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 🧩 reqsync

[![PyPI Version](https://img.shields.io/pypi/v/reqsync.svg)](https://pypi.org/project/reqsync/)
[![Python Versions](https://img.shields.io/pypi/pyversions/reqsync.svg)](https://pypi.org/project/reqsync/)
[![CI Status](https://github.com/ImYourBoyRoy/reqsync/actions/workflows/ci.yml/badge.svg)](https://github.com/ImYourBoyRoy/reqsync/actions/workflows/ci.yml)
[![License](https://img.shields.io/pypi/l/reqsync.svg)](https://opensource.org/licenses/MIT)
[![Typing: PEP 561](https://img.shields.io/badge/Typing-PEP%20561-informational.svg)](https://peps.python.org/pep-0561/)

> Upgrade a venv and rewrite requirements to match installed versions, safely and atomically. Preserves operators (`==`, `>=`, `~=`), extras, markers, comments, encoding, and line endings.

---

## ✨ Design goals

* **Safety first**
  Refuse outside a venv by default. Abort on `--hash` unless you opt in to skipping those stanzas. Atomic writes with rollback.

* **Do one job well**
  Upgrade env, then floor top-level requirements to what’s actually installed. No lockfile management.

* **Zero ceremony**
  Works from the CLI without any config. Optional TOML/pyproject config if you want it.

* **Agent friendly**
  Clean Python API that returns a structured `Result` for tool-calling, MCP, CrewAI, AG2, etc.

---

## ⭐ Features

* **Agent & CI Friendly**: Structured Python API and `--json-report` flag for easy integration into automated workflows.
* Venv guard on by default; `--system-ok` to override.
* Preserves extras `[a,b]`, markers `; sys_platform != "win32"`, inline comments, encoding (BOM), and newline style (LF/CRLF).
* Includes recursion: follows `-r other.txt` by default.
* Constraints awareness: detects `-c constraints.txt` and skips modifying unless `--update-constraints`.
* Policy modes: `lower-bound` (default), `floor-only`, `floor-and-cap` (`< next major`).
* Pre/dev handling: `--allow-prerelease` to adopt, `--keep-local` to keep `+local` suffixes.
* Hash-aware: refuses by default if `--hash` is present; `--allow-hashes` skips those stanzas without editing.
* File locking with `portalocker`. Backup + atomic replace. Rollback on failure.
* UX for humans and CI: `--dry-run`, `--show-diff`, `--check`, `--json-report`, `--only/--exclude`.
* Minimal runtime deps.

---

## πŸš€ Installation

```bash
pip install reqsync
```

> Requires Python 3.8+.

---

## πŸ§ͺ Quick start

```bash
# Activate your venv (required by default)
source .venv/bin/activate

# Preview
reqsync run --path requirements.txt --dry-run --show-diff

# Apply with backup
reqsync run --path requirements.txt --show-diff
```

Common variations:

```bash
# Read-only sync to current env (don’t run pip)
reqsync run --no-upgrade --show-diff

# CI gate: fail if changes would be made
reqsync run --check --no-upgrade --path requirements.txt
```

---

## πŸ› οΈ CLI

```bash
reqsync run [OPTIONS]
```

Key options:

* `--path PATH` target requirements (default `requirements.txt`)
* `--follow-includes / --no-follow-includes` follow `-r` recursively (default true)
* `--update-constraints` allow modifying constraint files (default false)
* `--policy [lower-bound|floor-only|floor-and-cap]` default `lower-bound`
* `--allow-prerelease` adopt pre/dev; `--keep-local` keep `+local`
* `--no-upgrade` skip `pip install -U -r`
* `--pip-args "..."` allowlisted pip flags (indexes, proxies, `-r`, `-c`)
* `--only PATTERNS` and `--exclude PATTERNS` scope by canonical names
* `--check` no writes; exit 11 if changes would be made
* `--dry-run` no writes; pair with `--show-diff` for unified diff
* `--json-report FILE` machine-readable changes
* `--backup-suffix S` `.bak` by default; `--timestamped-backups/--no-timestamped-backups`
* `--system-ok` allow outside a venv (not recommended)
* `--allow-hashes` skip hashed stanzas instead of refusing

Full option details: see [docs/USAGE.md](docs/USAGE.md).

---

## βš™οΈ Configuration

Config is optional. CLI flags always win.

Resolution order:

1. CLI flags
2. `reqsync.toml` at project root
3. `[tool.reqsync]` in `pyproject.toml`
4. `reqsync.json`
5. Built-in defaults

Examples: see [docs/CONFIG.md](docs/CONFIG.md) and `examples/`.

---

## 🧬 Policy modes

* **lower-bound**
  Always set `>= installed_version`.

* **floor-only**
  Only raise existing lower bounds. If none exists, leave it unchanged.

* **floor-and-cap**
  Set `>= installed_version,<next_major`. Useful guardrail in larger teams.

* **update-in-place**
  Preserve the original operator (`==`, `~=`, `>=`) and only update the version. Adds `>=` if no operator is present.

---

## πŸ“ Includes and constraints

* `-r` includes are followed (on by default) and processed with the same rules.
* `-c` constraints are detected but not modified unless you pass `--update-constraints`.

---

## 🧡 Preservation rules

* Lines that are comments, directives (`-r`, `-c`, `--index-url`, etc.), `-e/--editable`, VCS/URL/local paths are preserved and not edited.
* Inline comments are preserved. URLs containing `#` are handled safely.
* Encoding and newline style are preserved exactly.

---

## πŸ”’ Safety gates

* Refuses to run outside a venv unless `--system-ok`.
* Refuses to edit hashed files (`--hash=`) by default. Use `--allow-hashes` to skip those stanzas instead.
* File lock prevents concurrent edits. Atomic replace with backup and rollback.

---

## πŸ€– Python API (for agents/tools)

```python
from pathlib import Path
from reqsync._types import Options
from reqsync.core import sync

opts = Options(
    path=Path("requirements.txt"),
    follow_includes=True,
    policy="lower-bound",
    dry_run=True,
    show_diff=True,
    no_upgrade=True,
)
result = sync(opts)
print("Changed:", result.changed)
print(result.diff or "")
```

More integration patterns: [docs/INTEGRATION.md](docs/INTEGRATION.md).

---

## πŸ“Š Exit codes

| Code | Meaning                                           |
| ---: | ------------------------------------------------- |
|    0 | Success                                           |
|    1 | Generic error                                     |
|    2 | Requirements file not found                       |
|    3 | Hashes present without `--allow-hashes`           |
|    4 | Pip upgrade failed                                |
|    5 | Parse error                                       |
|    6 | Constraint conflict detected                      |
|    7 | Refused to run outside venv without `--system-ok` |
|    8 | Repo dirty and guard blocked (if enabled)         |
|    9 | Lock acquisition timeout                          |
|   10 | Write failed; backup restored                     |
|   11 | `--check` and changes would be made               |

---

## πŸ”„ How this compares

| Tool             | Primary goal               | Edits your file           | Hash-aware       | Lockfile |
| ---------------- | -------------------------- | ------------------------- | ---------------- | -------- |
| **reqsync**      | Sync to installed versions | Yes, preserves formatting | Refuses or skips | No       |
| pip-tools        | Deterministic pins         | Generates pinned file     | Yes              | Yes      |
| pip-upgrader/pur | Bump versions              | Rewrites pins (==)        | No               | No       |
| uv/poetry/pdm    | Env + lock mgmt            | Manage lockfiles          | Yes              | Yes      |

Use reqsync when you want your `requirements.txt` to reflect the versions you’re actually running, while keeping future installs flexible.

---

## πŸ“¦ Versioning & release

* Version is derived from Git tags via `hatch-vcs`. Tag format: `vX.Y.Z`.
* Repo: [https://github.com/ImYourBoyRoy/reqsync](https://github.com/ImYourBoyRoy/reqsync)
* CI: tests, lint, type-check, build, twine metadata check, wheel smoke test.
* Publishing: PyPI Trusted Publishing via OIDC on tags.

---

## 🧰 Development

```bash
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .[dev]
pre-commit install

pre-commit run -a
pytest -q
ruff check .
ruff format --check .
mypy src/reqsync
```

Contributing guidelines: [CONTRIBUTING.md](CONTRIBUTING.md)
Changelog: [CHANGELOG.md](CHANGELOG.md)

---

## πŸ“š Docs

* Usage: [docs/USAGE.md](docs/USAGE.md)
* Config: [docs/CONFIG.md](docs/CONFIG.md)
* Integration: [docs/INTEGRATION.md](docs/INTEGRATION.md)

---

## πŸ“œ License

MIT. See [LICENSE](LICENSE).

---

## ⭐ Support

If this tool helps you, star the repo and share it. Issues and PRs welcome.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "reqsync",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "agent, ai-tool, automation, ci-cd, cli-tool, dependencies, dependency-management, devops, packaging, pip, requirements, venv",
    "author": null,
    "author_email": "ImYourBoyRoy <roy.dawson.iv@gmail.com.com>",
    "download_url": "https://files.pythonhosted.org/packages/0e/ff/dcd4c8f0d4d8c2463b7b4ef357cadc854d9da1e95422c2a9e383c2bc998e/reqsync-0.1.2.tar.gz",
    "platform": null,
    "description": "# \ud83e\udde9 reqsync\n\n[![PyPI Version](https://img.shields.io/pypi/v/reqsync.svg)](https://pypi.org/project/reqsync/)\n[![Python Versions](https://img.shields.io/pypi/pyversions/reqsync.svg)](https://pypi.org/project/reqsync/)\n[![CI Status](https://github.com/ImYourBoyRoy/reqsync/actions/workflows/ci.yml/badge.svg)](https://github.com/ImYourBoyRoy/reqsync/actions/workflows/ci.yml)\n[![License](https://img.shields.io/pypi/l/reqsync.svg)](https://opensource.org/licenses/MIT)\n[![Typing: PEP 561](https://img.shields.io/badge/Typing-PEP%20561-informational.svg)](https://peps.python.org/pep-0561/)\n\n> Upgrade a venv and rewrite requirements to match installed versions, safely and atomically. Preserves operators (`==`, `>=`, `~=`), extras, markers, comments, encoding, and line endings.\n\n---\n\n## \u2728 Design goals\n\n* **Safety first**\n  Refuse outside a venv by default. Abort on `--hash` unless you opt in to skipping those stanzas. Atomic writes with rollback.\n\n* **Do one job well**\n  Upgrade env, then floor top-level requirements to what\u2019s actually installed. No lockfile management.\n\n* **Zero ceremony**\n  Works from the CLI without any config. Optional TOML/pyproject config if you want it.\n\n* **Agent friendly**\n  Clean Python API that returns a structured `Result` for tool-calling, MCP, CrewAI, AG2, etc.\n\n---\n\n## \u2b50 Features\n\n* **Agent & CI Friendly**: Structured Python API and `--json-report` flag for easy integration into automated workflows.\n* Venv guard on by default; `--system-ok` to override.\n* Preserves extras `[a,b]`, markers `; sys_platform != \"win32\"`, inline comments, encoding (BOM), and newline style (LF/CRLF).\n* Includes recursion: follows `-r other.txt` by default.\n* Constraints awareness: detects `-c constraints.txt` and skips modifying unless `--update-constraints`.\n* Policy modes: `lower-bound` (default), `floor-only`, `floor-and-cap` (`< next major`).\n* Pre/dev handling: `--allow-prerelease` to adopt, `--keep-local` to keep `+local` suffixes.\n* Hash-aware: refuses by default if `--hash` is present; `--allow-hashes` skips those stanzas without editing.\n* File locking with `portalocker`. Backup + atomic replace. Rollback on failure.\n* UX for humans and CI: `--dry-run`, `--show-diff`, `--check`, `--json-report`, `--only/--exclude`.\n* Minimal runtime deps.\n\n---\n\n## \ud83d\ude80 Installation\n\n```bash\npip install reqsync\n```\n\n> Requires Python 3.8+.\n\n---\n\n## \ud83e\uddea Quick start\n\n```bash\n# Activate your venv (required by default)\nsource .venv/bin/activate\n\n# Preview\nreqsync run --path requirements.txt --dry-run --show-diff\n\n# Apply with backup\nreqsync run --path requirements.txt --show-diff\n```\n\nCommon variations:\n\n```bash\n# Read-only sync to current env (don\u2019t run pip)\nreqsync run --no-upgrade --show-diff\n\n# CI gate: fail if changes would be made\nreqsync run --check --no-upgrade --path requirements.txt\n```\n\n---\n\n## \ud83d\udee0\ufe0f CLI\n\n```bash\nreqsync run [OPTIONS]\n```\n\nKey options:\n\n* `--path PATH` target requirements (default `requirements.txt`)\n* `--follow-includes / --no-follow-includes` follow `-r` recursively (default true)\n* `--update-constraints` allow modifying constraint files (default false)\n* `--policy [lower-bound|floor-only|floor-and-cap]` default `lower-bound`\n* `--allow-prerelease` adopt pre/dev; `--keep-local` keep `+local`\n* `--no-upgrade` skip `pip install -U -r`\n* `--pip-args \"...\"` allowlisted pip flags (indexes, proxies, `-r`, `-c`)\n* `--only PATTERNS` and `--exclude PATTERNS` scope by canonical names\n* `--check` no writes; exit 11 if changes would be made\n* `--dry-run` no writes; pair with `--show-diff` for unified diff\n* `--json-report FILE` machine-readable changes\n* `--backup-suffix S` `.bak` by default; `--timestamped-backups/--no-timestamped-backups`\n* `--system-ok` allow outside a venv (not recommended)\n* `--allow-hashes` skip hashed stanzas instead of refusing\n\nFull option details: see [docs/USAGE.md](docs/USAGE.md).\n\n---\n\n## \u2699\ufe0f Configuration\n\nConfig is optional. CLI flags always win.\n\nResolution order:\n\n1. CLI flags\n2. `reqsync.toml` at project root\n3. `[tool.reqsync]` in `pyproject.toml`\n4. `reqsync.json`\n5. Built-in defaults\n\nExamples: see [docs/CONFIG.md](docs/CONFIG.md) and `examples/`.\n\n---\n\n## \ud83e\uddec Policy modes\n\n* **lower-bound**\n  Always set `>= installed_version`.\n\n* **floor-only**\n  Only raise existing lower bounds. If none exists, leave it unchanged.\n\n* **floor-and-cap**\n  Set `>= installed_version,<next_major`. Useful guardrail in larger teams.\n\n* **update-in-place**\n  Preserve the original operator (`==`, `~=`, `>=`) and only update the version. Adds `>=` if no operator is present.\n\n---\n\n## \ud83d\udcc1 Includes and constraints\n\n* `-r` includes are followed (on by default) and processed with the same rules.\n* `-c` constraints are detected but not modified unless you pass `--update-constraints`.\n\n---\n\n## \ud83e\uddf5 Preservation rules\n\n* Lines that are comments, directives (`-r`, `-c`, `--index-url`, etc.), `-e/--editable`, VCS/URL/local paths are preserved and not edited.\n* Inline comments are preserved. URLs containing `#` are handled safely.\n* Encoding and newline style are preserved exactly.\n\n---\n\n## \ud83d\udd12 Safety gates\n\n* Refuses to run outside a venv unless `--system-ok`.\n* Refuses to edit hashed files (`--hash=`) by default. Use `--allow-hashes` to skip those stanzas instead.\n* File lock prevents concurrent edits. Atomic replace with backup and rollback.\n\n---\n\n## \ud83e\udd16 Python API (for agents/tools)\n\n```python\nfrom pathlib import Path\nfrom reqsync._types import Options\nfrom reqsync.core import sync\n\nopts = Options(\n    path=Path(\"requirements.txt\"),\n    follow_includes=True,\n    policy=\"lower-bound\",\n    dry_run=True,\n    show_diff=True,\n    no_upgrade=True,\n)\nresult = sync(opts)\nprint(\"Changed:\", result.changed)\nprint(result.diff or \"\")\n```\n\nMore integration patterns: [docs/INTEGRATION.md](docs/INTEGRATION.md).\n\n---\n\n## \ud83d\udcca Exit codes\n\n| Code | Meaning                                           |\n| ---: | ------------------------------------------------- |\n|    0 | Success                                           |\n|    1 | Generic error                                     |\n|    2 | Requirements file not found                       |\n|    3 | Hashes present without `--allow-hashes`           |\n|    4 | Pip upgrade failed                                |\n|    5 | Parse error                                       |\n|    6 | Constraint conflict detected                      |\n|    7 | Refused to run outside venv without `--system-ok` |\n|    8 | Repo dirty and guard blocked (if enabled)         |\n|    9 | Lock acquisition timeout                          |\n|   10 | Write failed; backup restored                     |\n|   11 | `--check` and changes would be made               |\n\n---\n\n## \ud83d\udd04 How this compares\n\n| Tool             | Primary goal               | Edits your file           | Hash-aware       | Lockfile |\n| ---------------- | -------------------------- | ------------------------- | ---------------- | -------- |\n| **reqsync**      | Sync to installed versions | Yes, preserves formatting | Refuses or skips | No       |\n| pip-tools        | Deterministic pins         | Generates pinned file     | Yes              | Yes      |\n| pip-upgrader/pur | Bump versions              | Rewrites pins (==)        | No               | No       |\n| uv/poetry/pdm    | Env + lock mgmt            | Manage lockfiles          | Yes              | Yes      |\n\nUse reqsync when you want your `requirements.txt` to reflect the versions you\u2019re actually running, while keeping future installs flexible.\n\n---\n\n## \ud83d\udce6 Versioning & release\n\n* Version is derived from Git tags via `hatch-vcs`. Tag format: `vX.Y.Z`.\n* Repo: [https://github.com/ImYourBoyRoy/reqsync](https://github.com/ImYourBoyRoy/reqsync)\n* CI: tests, lint, type-check, build, twine metadata check, wheel smoke test.\n* Publishing: PyPI Trusted Publishing via OIDC on tags.\n\n---\n\n## \ud83e\uddf0 Development\n\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -U pip\npip install -e .[dev]\npre-commit install\n\npre-commit run -a\npytest -q\nruff check .\nruff format --check .\nmypy src/reqsync\n```\n\nContributing guidelines: [CONTRIBUTING.md](CONTRIBUTING.md)\nChangelog: [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## \ud83d\udcda Docs\n\n* Usage: [docs/USAGE.md](docs/USAGE.md)\n* Config: [docs/CONFIG.md](docs/CONFIG.md)\n* Integration: [docs/INTEGRATION.md](docs/INTEGRATION.md)\n\n---\n\n## \ud83d\udcdc License\n\nMIT. See [LICENSE](LICENSE).\n\n---\n\n## \u2b50 Support\n\nIf this tool helps you, star the repo and share it. Issues and PRs welcome.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Synchronize requirements.txt to match installed versions, safely and atomically.",
    "version": "0.1.2",
    "project_urls": {
        "Bug Tracker": "https://github.com/ImYourBoyRoy/reqsync/issues",
        "Homepage": "https://github.com/ImYourBoyRoy/reqsync",
        "Repository": "https://github.com/ImYourBoyRoy/reqsync"
    },
    "split_keywords": [
        "agent",
        " ai-tool",
        " automation",
        " ci-cd",
        " cli-tool",
        " dependencies",
        " dependency-management",
        " devops",
        " packaging",
        " pip",
        " requirements",
        " venv"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "69736fdc21ad3dabea678d331a7ddc02cc18eab202ffd1166c6b924abb35fabb",
                "md5": "3aa031150d90f1302f821a92d03ea612",
                "sha256": "fcad7cd7bc1579318b3b5d8d92bb3b86a1dc2f67f932530b419a72a01d383b54"
            },
            "downloads": -1,
            "filename": "reqsync-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3aa031150d90f1302f821a92d03ea612",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 19734,
            "upload_time": "2025-08-20T06:34:34",
            "upload_time_iso_8601": "2025-08-20T06:34:34.587890Z",
            "url": "https://files.pythonhosted.org/packages/69/73/6fdc21ad3dabea678d331a7ddc02cc18eab202ffd1166c6b924abb35fabb/reqsync-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0effdcd4c8f0d4d8c2463b7b4ef357cadc854d9da1e95422c2a9e383c2bc998e",
                "md5": "773e3eacd138464b1f9fb9280cdb3c8b",
                "sha256": "0164e55e684c7437647441d1f4d3e32d6cb089e7f1d297e526fd200b9041fbfc"
            },
            "downloads": -1,
            "filename": "reqsync-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "773e3eacd138464b1f9fb9280cdb3c8b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 23096,
            "upload_time": "2025-08-20T06:34:36",
            "upload_time_iso_8601": "2025-08-20T06:34:36.489747Z",
            "url": "https://files.pythonhosted.org/packages/0e/ff/dcd4c8f0d4d8c2463b7b4ef357cadc854d9da1e95422c2a9e383c2bc998e/reqsync-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-20 06:34:36",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ImYourBoyRoy",
    "github_project": "reqsync",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "typer",
            "specs": [
                [
                    ">=",
                    "0.16.1"
                ]
            ]
        },
        {
            "name": "packaging",
            "specs": [
                [
                    ">=",
                    "25.0"
                ]
            ]
        },
        {
            "name": "portalocker",
            "specs": [
                [
                    ">=",
                    "3.2.0"
                ]
            ]
        },
        {
            "name": "tomli",
            "specs": [
                [
                    ">=",
                    "2.2.1"
                ]
            ]
        },
        {
            "name": "typing-extensions",
            "specs": [
                [
                    ">=",
                    "4.14.1"
                ]
            ]
        },
        {
            "name": "rich",
            "specs": [
                [
                    ">=",
                    "14.1.0"
                ]
            ]
        }
    ],
    "lcname": "reqsync"
}
        
Elapsed time: 1.97694s