# josh-sync Bidirectional monorepo ↔ subrepo sync via [josh-proxy](https://josh-project.github.io/josh/). Supports multiple sync targets from a single config. ## Quick Start ### 1. Add config Create `.josh-sync.yml` in your monorepo root: ```yaml josh: proxy_url: "https://josh.example.com" monorepo_path: "org/monorepo" targets: - name: "billing" subfolder: "services/billing" josh_filter: ":/services/billing" subrepo_url: "git@gitea.example.com:ext/billing.git" subrepo_auth: "ssh" branches: main: main forward_only: [] bot: name: "josh-sync-bot" email: "josh-sync-bot@example.com" trailer: "Josh-Sync-Origin" ``` ### 2. Add CI workflows Copy from [examples/](examples/) and customize paths/branches: ```yaml # .gitea/workflows/josh-sync-forward.yml - uses: https://your-gitea.example.com/org/josh-sync@v1 with: direction: forward env: SYNC_BOT_USER: ${{ secrets.SYNC_BOT_USER }} SYNC_BOT_TOKEN: ${{ secrets.SYNC_BOT_TOKEN }} SUBREPO_SSH_KEY: ${{ secrets.SUBREPO_SSH_KEY }} ``` ### 3. Local dev (Nix) Add josh-sync as a flake input, then: ```nix { inputs, ... }: { imports = [ inputs.josh-sync.devenvModules.default ]; } ``` Run `josh-sync preflight` to validate your setup. ## CLI ``` josh-sync sync [--forward|--reverse] [--target NAME[,NAME]] [--branch BRANCH] josh-sync preflight josh-sync import josh-sync reset josh-sync status josh-sync state show [branch] josh-sync state reset [branch] ``` ## How It Works - **Forward sync** (mono → subrepo): pushes directly if clean, creates conflict PR if not. Uses `--force-with-lease` for safety. - **Reverse sync** (subrepo → mono): always creates a PR, never pushes directly. - **Loop prevention**: `Josh-Sync-Origin:` git trailer filters out bot commits. - **State tracking**: orphan branch `josh-sync-state` stores JSON per target/branch. ## Dependencies `bash >=4`, `git`, `curl`, `jq`, `yq` ([mikefarah/yq](https://github.com/mikefarah/yq) v4+), `openssh` ## License MIT