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 (required)

Target Required

A target must be set at the global level, per-repo level, or per-source level. If no target is found at any level, sync will error:

# Error: Repo 'myrepo' has no target configured
syncware sync

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.9.tar.gz (10.8 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.9-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: syncware-0.0.9.tar.gz
  • Upload date:
  • Size: 10.8 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.9.tar.gz
Algorithm Hash digest
SHA256 99fa45a313a200fbacd82a4798e11013aac8e21ef8d87441b61c9de5f521080f
MD5 61dc62451e6665ab875b55e59541dcc2
BLAKE2b-256 0f81459fdd69ddfce469ea6d6802a6384d7f0134364712e2d12323b0cb203536

See more details on using hashes here.

File details

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

File metadata

  • Download URL: syncware-0.0.9-py3-none-any.whl
  • Upload date:
  • Size: 14.5 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.9-py3-none-any.whl
Algorithm Hash digest
SHA256 fa95bf9e4fffa875697768ee5dda66ed7031104392dc1f010c770556e1d7db1c
MD5 d2fcf88553b0e6c1a4fe8f6e17bc1aaf
BLAKE2b-256 1e00137b7bd2416ba92d9b1afbcdac4550da8d66f469f578136f4d602d18a5e7

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