Skip to main content

Sync selective folders from Git repositories to a target directory

Project description

syncware

A CLI tool for syncing selective folders from local Git repositories to a target directory. Think go mod vendor or npm install, but for folder-based content.

Installation

pip install syncware

Development

Clone the repository and install with dev dependencies using uv:

git clone https://github.com/youruser/syncware.git
cd syncware
uv sync --group dev

This installs build, twine, and pytest for building and testing.

Quick Start

# Initialize a new project
syncware init

# Edit syncware.yml with your repositories

# List configured folders
syncware list

# Sync folders to target
syncware sync

# Preview sync (dry run)
syncware sync --dry

# Update repos and sync
syncware update

# Generate lockfile (captures exact versions)
syncware lockfile

# Verify folders against lockfile
syncware verify

# Auto-fix drift
syncware verify --fix

Verbosity

syncware -v list      # INFO level (default)
syncware -vv list     # DEBUG level

Configuration

Create a syncware.yml file in your project root:

repos:
  - name: myrepo
    path: /path/to/local/repo
    source: path/to/folders
    items:
      - folder1
      - file.txt
      - "*"  # or all items (files and subdirectories)
    target: ./output  # optional, overrides global target

target: ./output  # global target (default)

Default Target

If no target is specified globally or per-repo, it defaults to ./output:

# Both of these are equivalent:
syncware sync              # uses ./output as default
target: ./output
syncware sync             # explicitly set

Per-Repo Target

Each repo can have its own target:

repos:
  - name: frontend
    path: ./packages/frontend
    source: dist
    items: ["ui", "icons"]
    target: ./public/assets

  - name: backend
    path: ./packages/backend
    source: configs
    items: ["nginx", "docker"]
    target: ./deploy/config

target: ./common/output  # used by repos without own target

Multiple Sources Per Repo

A single repo entry can sync from multiple source directories, each with its own items and optional per-source target:

repos:
  - name: myrepo
    path: /path/to/repo
    sources:
      - source: skills
        items: ["folder1", "folder2"]
        target: ./output1  # optional, overrides repo/global target
      - source: docs
        items: ["readme", "api"]
        target: ./output2

This is equivalent to:

# Legacy single-source format (still fully supported)
repos:
  - name: myrepo
    path: /path/to/repo
    source: skills
    items: ["folder1", "folder2"]
    target: ./output1

YAML Variables (Anchor & Alias)

YAML anchors and aliases let you define a value once and reuse it across multiple repos:

# Define reusable values
shared_target: &shared_target ~/Downloads/output
backup_target: &backup_target ./backup

repos:
  - name: frontend
    path: ./packages/frontend
    source: dist
    items: ["ui", "icons"]
    target: *shared_target

  - name: backend
    path: ./packages/backend
    source: configs
    items: ["nginx", "docker"]
    target: *shared_target

  - name: docs
    path: ./docs
    source: build
    items: ["html"]
    target: *backup_target

Clean Item

When clean_item_content: true, synchronizes target folders to exactly match source by removing any extra files/directories that exist in target but not in source. Can be set globally or per-repo:

clean_item_content: true  # global setting

repos:
  - name: myrepo
    path: ./repo
    source: dist
    items: ["ui", "icons"]
    clean_item_content: false  # repo-level override

Ignore Hidden Files and Folders

By default, all files are synced including hidden ones (starting with .). Set ignore_hidden_file or ignore_hidden_folder to true to exclude them:

ignore_hidden_file: true   # skip hidden files (starting with .)
ignore_hidden_folder: true  # skip hidden directories (starting with .)

repos:
  - name: myrepo
    path: ./repo
    source: dist
    items: ["ui", "icons"]
    ignore_hidden_file: false  # repo-level override

Keep Existing Item

When keep_existing_item: false, removes folders in the target root that aren't configured to sync. Default is true (keeps extra folders):

keep_existing_item: false  # global setting

repos:
  - name: myrepo
    path: ./repo
    source: dist
    items: ["ui", "icons"]
    keep_existing_item: true  # repo-level override

Multiple Targets

Both global and per-repo targets can specify multiple destinations:

# Global multiple targets
target: ["./output1", "./output2"]

repos:
  - name: myrepo
    path: /path/to/repo
    source: dist
    items: ["ui", "icons"]
    # folder1 and folder2 sync to BOTH ./output1 and ./output2
# Per-repo multiple targets
repos:
  - name: frontend
    path: ./packages/frontend
    source: dist
    items: ["ui", "icons"]
    target: ["./public/assets", "./backup/assets"]
    # ui and icons sync to BOTH ./public/assets and ./backup/assets

Remote Repositories

You can also use remote Git URLs (GitHub, GitLab, etc.):

repos:
  - name: claude
    path: https://github.com/user/repo.git
    source: skills
    items: ["docx", "xlsx"]

Remote repos are cloned to ~/.cache/syncware/<name> and reused on subsequent runs.

Config Fields

Field Description
repos List of source repositories
repos[].name Namespace identifier
repos[].path Local path or Git URL
repos[].source Subdirectory containing folders to sync (single-source format)
repos[].sources List of source entries, each with source, items, optional target (multi-source format)
repos[].items List of folder or file names, or "*" for all
repos[].target Per-repo destination (string or list, overrides global)
repos[].clean_item_content Remove extra files in synced folders not in source (overrides global)
clean_item_content Global clean setting (removes extra files in synced folders)
repos[].ignore_hidden_file Skip hidden files starting with . (overrides global)
ignore_hidden_file Global ignore hidden files setting
repos[].ignore_hidden_folder Skip hidden directories starting with . (overrides global)
ignore_hidden_folder Global ignore hidden folders setting
repos[].keep_existing_item Keep extra folders in target root not in config (overrides global)
keep_existing_item Global keep setting (removes extra folders in target root)
target Global destination directory (string or list)

How It Works

<repo.path>/<source>/<folder> → copied to <repo.target>/<folder>
                                  or <global.target>/<folder>
                                  (or to ALL targets if multiple)

Lockfile

The lockfile (syncware.lock.yml) ensures reproducible syncs:

syncware lockfile          # Generate lockfile
syncware verify           # Check for drift
syncware verify --fix     # Auto-fix drift

What gets locked:

  • Git commit hash of each source repository
  • Content hash (SHA256) of each synced folder

Verification detects:

  • DRIFT - Folder content was modified locally
  • UPDATE_AVAILABLE - Source repo has new commits
  • MISSING - Source folder was deleted

Use Cases

  • Skills/Plugins - Sync skill folders from a central repo
  • Config Management - Keep config folders in sync across machines
  • Documentation - Maintain a set of doc folders from different sources
  • Assets - Bundle asset folders from multiple repositories

Comparison

Tool Use Case
syncware Selective folder sync from local or remote repos
go mod vendor Vendor Go dependencies
npm install Install package dependencies
rsync General file/folder synchronization

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

syncware-0.0.8.tar.gz (10.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

syncware-0.0.8-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file syncware-0.0.8.tar.gz.

File metadata

  • Download URL: syncware-0.0.8.tar.gz
  • Upload date:
  • Size: 10.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for syncware-0.0.8.tar.gz
Algorithm Hash digest
SHA256 4e02632c74e5f81fa6ea7ecedbc4289f12c3da94a78aa3b0c25bfad284ab9999
MD5 d4a2b00f3dc2acd7498663d0d46085d3
BLAKE2b-256 ae01deb8cf0167bb4c628b2271c13e4fd0ff97a045ed157fbadf9e26efabb85c

See more details on using hashes here.

File details

Details for the file syncware-0.0.8-py3-none-any.whl.

File metadata

  • Download URL: syncware-0.0.8-py3-none-any.whl
  • Upload date:
  • Size: 14.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for syncware-0.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 e8e766b9c997b3eecbf83e20d8cbac8fa57be226b06a7d781771df90385d3a3d
MD5 707bb195f0641b42ef350cace2fca94f
BLAKE2b-256 d63eea38c24956ea5c007136375f7e4ec48166ddc7b73f13400f9753ac0c4b26

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page