Folds `josh-sync adopt` into `josh-sync onboard <target>` as the adopt strategy alongside the original reset flow. Strategy resolves by precedence: --mode flag > targets[].history_lock config (preserve|rewrite) > auto-detect via `git ls-remote --heads`. `josh-sync adopt` is kept as a thin alias. Adds the new `targets[].history_lock` config field (validated at parse time) and folds `<target>/adopt.json` state into `<target>/onboard.json` with a `.strategy` field; legacy state files are read with a backward-compat fallback. Safety hardening (from a multi-angle review of the unification): - auto-detect distinguishes auth/network failure from empty repo - resume validates --mode against the strategy saved in state - `josh-sync adopt` rejects a conflicting --mode in the forwarded args - missing import-PR lookup dies in both strategies (was WARN+continue for reset, which could create duplicate import PRs on resume) - --restart durably removes the legacy adopt.json from the state branch - adopt_branch is now a subshell function (EXIT trap can't clobber callers) - strategy value validated after resolve/load (reset|adopt) - --mode with missing/empty value dies with a usage hint - migrate-pr against an adopt-strategy target dies with a specific hint - reset importing asserts archived_url is present (no "null" → git clone) End-to-end + CLI bats coverage added (tests/unit/adopt_e2e.bats, tests/unit/cli.bats). 72 tests, shellcheck clean. Makefile dist bundle header now correctly interpolates VERSION and line count. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.1 KiB
5.1 KiB
Configuration Reference
Full reference for .josh-sync.yml fields and environment variables.
.josh-sync.yml Structure
schema_version: 2 # required since v2.0.0
josh: # josh-proxy + monorepo settings (required)
targets: # sync targets (required, at least 1)
bot: # bot identity for sync commits (required)
schema_version
| Type | Required | Description |
|---|---|---|
| integer | Yes | Must be 2 for josh-sync v2.x configs. v1 configs are rejected; see Changelog v2.0.0 for migration. |
josh Section
| Field | Type | Required | Description |
|---|---|---|---|
proxy_url |
string | Yes | Josh-proxy URL, no trailing slash. Must start with http:// or https://. |
monorepo_path |
string | Yes | Repository path as josh-proxy sees it (e.g., org/monorepo). |
monorepo_url |
string | Yes | Monorepo git clone URL — git@host:org/repo.git (SSH), ssh://..., or https://host/org/repo.git. The gitea host is derived from this URL and used for the PR API and direct clones, decoupled from any target's host. |
monorepo_auth |
string | No (default "https") |
Clone auth method: "https" (injects ${SYNC_BOT_USER}:${SYNC_BOT_TOKEN} into the URL) or "ssh" (uses the user's ssh-agent / ~/.ssh/config). PR creation always uses GITEA_TOKEN regardless. |
targets[] Section
Each target maps a monorepo subfolder to an external subrepo.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | Yes | — | Unique target identifier. Used in CLI commands and state tracking. |
subfolder |
string | Yes | — | Monorepo subfolder path (e.g., services/billing). |
josh_filter |
string | No | :/<subfolder> |
Josh filter expression. Auto-derived from subfolder if omitted. Must start with :. |
subrepo_url |
string | Yes | — | External subrepo Git URL. Supports HTTPS (https://...), SSH (git@host:path), and ssh:// formats. |
subrepo_auth |
string | No | "https" |
Auth method: "https" or "ssh". |
subrepo_token_var |
string | No | "SUBREPO_TOKEN" |
Name of the env var holding the HTTPS token for this target. |
subrepo_ssh_key_var |
string | No | "SUBREPO_SSH_KEY" |
Name of the env var holding the SSH private key for this target. |
branches |
object | Yes | — | Branch mapping: mono_branch: subrepo_branch. Each key-value pair syncs those branches bidirectionally. |
forward_only |
string[] | No | [] |
Branches that only sync mono → subrepo, never reverse. |
exclude |
string[] | No | [] |
File/directory patterns to exclude from sync via josh :exclude filter. Excluded files exist only in the monorepo, never in the subrepo. See Excluding Files. |
history_lock |
string | No | (auto-detect) | Onboarding strategy hint. "preserve" selects the non-destructive adopt strategy (keeps subrepo history via an adoption merge — ADR-013); "rewrite" selects the destructive reset strategy (force-pushes josh-filtered history). When omitted, josh-sync onboard auto-detects: subrepos with branches → adopt, empty subrepos → reset. The CLI flag --mode={reset,adopt} overrides this. |
bot Section
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Git author name for sync commits. |
email |
string | Yes | Git author email for sync commits. |
trailer |
string | Yes | Git trailer key for loop prevention (e.g., Josh-Sync-Origin). |
Environment Variables
Credentials
| Variable | Purpose | Default |
|---|---|---|
SYNC_BOT_USER |
Bot's Git username | — |
SYNC_BOT_TOKEN |
API token for monorepo access and josh-proxy HTTPS auth | — |
SUBREPO_TOKEN |
HTTPS token for subrepo access | Falls back to SYNC_BOT_TOKEN |
SUBREPO_SSH_KEY |
SSH private key content (not a file path) for subrepo access | — |
Per-target credential overrides
Set subrepo_token_var or subrepo_ssh_key_var in a target's config to read credentials from a different env var:
targets:
- name: "auth"
subrepo_token_var: "AUTH_REPO_TOKEN" # reads from $AUTH_REPO_TOKEN
subrepo_ssh_key_var: "AUTH_SSH_KEY" # reads from $AUTH_SSH_KEY
Resolution order: per-target env var → default env var (SUBREPO_TOKEN / SUBREPO_SSH_KEY) → SYNC_BOT_TOKEN fallback.
Runtime
| Variable | Purpose | Default |
|---|---|---|
JOSH_SYNC_TARGET |
Restrict sync to a single target | All targets |
JOSH_SYNC_STATE_BRANCH |
Name of the orphan branch for state storage | josh-sync-state |
JOSH_SYNC_DEBUG |
Enable verbose logging (1 to enable) |
0 |
MONOREPO_API |
Override the monorepo gitea PR API URL. Escape hatch for custom API endpoints; rarely needed. | https://{monorepo_url's host}/api/v1/repos/{monorepo_path} |
JSON Schema
The config file can be validated against the JSON Schema at schema/config-schema.json.