Duck Syncยถ
Dependency sync tool for Duck Framework projects. Reads duck.toml and installs
Python and system packages in one command, across any platform.
Installationยถ
Duck Sync ships with Duck Framework. No separate install needed.
duck.tomlยถ
Place duck.toml at your project root and commit it to version control.
# duck.toml
[dependencies]
# Python packages (pip/uv)
python = ["requests", "psycopg2"]
# System packages (apt, brew, pacman, etc.)
# Write generic names โ Duck Sync resolves per platform.
system = ["postgresql-client", "gdal"]
# Optional: requirements.txt installed alongside python list
# requirements = "requirements.txt"
# Optional: extra args for every pip/uv install in this group
# python_args = ["--no-cache-dir"]
# Optional: extra args for every system install in this group
# system_args = ["--no-install-recommends"]
[development]
# Only installed when --dev or --with-dev is passed
python = ["pytest", "ruff", "mypy"]
system = []
[sync]
# Pin sudo behaviour (optional โ auto-detected by default)
# true = always use sudo for system installs
# false = never use sudo (e.g. already root inside a container)
# use_sudo = true
[overrides]
# Override the resolved package name for a specific backend.
#
# [overrides.mypackage]
# apt = "libmypackage-dev"
# brew = "mypackage"
# pacman = "mypackage"
Commandsยถ
duck syncยถ
Install project dependencies.
duck sync [OPTIONS]
Option |
Short |
Description |
|---|---|---|
|
Install |
|
|
|
Install |
|
Print planned installs without running them |
|
|
|
Explicit path to |
|
Extra args appended to every pip/uv install |
|
|
Extra args appended to every system install |
|
|
Force sudo on or off for system installs |
|
|
|
Override the auto-detected package manager |
Examplesยถ
# Install [dependencies] only
duck sync
# Install [development] only
duck sync --dev
# Install everything
duck sync --with-dev
# Preview without installing
duck sync --dry-run
duck sync --with-dev --dry-run
# Use a specific package manager
duck sync --backend brew
duck sync --backend pacman
duck sync --backend "some_command -i"
# Force sudo
duck sync --sudo
duck sync --no-sudo
# Pass extra pip flags
duck sync --pip-args "--no-cache-dir"
duck sync --pip-args "--extra-index-url https://download.pytorch.org/whl/cu118"
# Pass extra system flags
duck sync --system-args "--no-install-recommends"
# Point to a different config
duck sync -c path/to/duck.toml
Package name resolutionยถ
System packages are written as generic names in duck.toml. Duck Sync maps them
to the correct name for the active package manager automatically.
Generic name |
apt |
brew |
dnf |
pacman |
choco |
|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For any package not in the built-in registry, the generic name is passed through unchanged. Most packages share the same name across managers so this works fine.
Overriding a nameยถ
Add an [overrides] block to duck.toml to override for any backend:
[overrides.mypackage]
apt = "libmypackage-dev"
brew = "mypackage"
pacman = "mypackage"
Backend selectionยถ
Duck Sync detects the system package manager automatically:
Platform |
Detection |
|---|---|
macOS |
|
Debian / Ubuntu |
|
Fedora / RHEL |
|
Arch Linux |
|
Windows |
|
Android (Termux) |
|
Override detection for a single run with --backend:
# Force a known backend
duck sync --backend apt
# Use a completely custom install command
# The package name is appended as the last argument
duck sync --backend "nix-env -iA nixpkgs"
duck sync --backend "some_command -i"
Sudo behaviourยถ
Situation |
Behaviour |
|---|---|
Running as a normal user |
|
Running as root (e.g. inside a container) |
|
|
Always use |
|
Never use |
|
Force |
|
Skip |
Precedence (highest to lowest): CLI flag โ duck.toml โ auto-detect.
Backends that never use sudo (brew, choco, pkg) ignore this setting entirely.
Per-package extra argsยถ
Override install args for a specific package inside a group:
[dependencies]
python = ["torch", "requests"]
[dependencies.python_package_args]
torch = ["--index-url", "https://download.pytorch.org/whl/cu118"]
[dependencies.system_package_args]
gdal = ["--no-install-recommends"]
Group-level args (python_args, system_args) are applied first, then
per-package args, then any --pip-args / --system-args passed on the CLI.
Errorsยถ
Error |
Cause |
|---|---|
|
No |
|
|
|
OS is unrecognised or no supported package manager is on PATH |
|
A package manager command exited non-zero |
All errors inherit from DuckSyncError, so you can catch them in one place:
from duck.cli.commands.sync.exceptions import DuckSyncError
from duck.cli.commands.sync.sync import DuckSync
try:
DuckSync().run()
except DuckSyncError as e:
print(f"Sync failed: {e}")