- Add v1.1.0 and v1.2.0 changelog entries - Add exclude field to config reference and example config - Add ADRs documenting all major design decisions - Fix step numbering in reverse_sync() - Fix action.yml to copy VERSION file - Add dist/ and .env to .gitignore - Use refs/tags/ format for Nix flake tag refs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2.7 KiB
ADR-006: Inline Exclude in Josh-Proxy URL
Status: Accepted Date: 2026-02
Context
Some files in a monorepo subfolder should not appear in the subrepo (e.g., monorepo-specific CI configs, internal tooling, secrets templates). We need a mechanism to exclude these files from sync.
Alternatives considered
-
.josh-sync-excludefile committed to the repo: A gitignore-style file listing patterns. Requires generating and committing a file. Changes to the exclude list create commits. The file itself would need to be excluded from the subrepo (circular dependency). -
Post-clone file deletion: Clone through josh, then
rm -rfexcluded paths before pushing. Fragile — deletions create diff noise. Doesn't work for reverse sync (excluded files would appear as "deleted" in the subrepo). -
Josh
:excludefilter inline in the URL: Josh-proxy supports:exclude[::pattern1,::pattern2]appended to the filter path. The exclusion happens at the transport layer — git objects for excluded files are never transferred. Works identically for clone (forward) and push (reverse). -
Separate josh filter file: Generate a josh filter expression and store it somewhere. Adds state management complexity.
Decision
Embed exclusion patterns inline in the josh-proxy URL using josh's native :exclude syntax. The exclude config field in .josh-sync.yml is transformed at config parse time into the josh filter string.
Example
Config:
exclude:
- ".monorepo/"
- "**/internal/"
Produces josh filter:
:/services/billing:exclude[::.monorepo/,::**/internal/]
Implementation
The parse_config() function in lib/config.sh uses jq to conditionally append :exclude[...] to the josh filter when the exclude array is non-empty. The enriched filter is stored in JOSH_SYNC_TARGETS JSON and used everywhere via $JOSH_FILTER.
Consequences
Positive:
- Zero committed files — exclusion is purely in the URL
- Transport-layer filtering — excluded content never leaves the git server
- Works identically for forward sync (clone), reverse sync (push), and reset
- Tree comparison (
skipdetection) works correctly since excluded files aren't in the filtered view - Standard josh syntax — no custom invention
Negative:
- Josh's
:excludepattern syntax is limited (no negation, no regex — only glob-style patterns with::prefix) - Long exclude lists make the URL unwieldy (though this is cosmetic — git handles long URLs fine)
- Changing the exclude list changes the josh filter, which changes all filtered SHAs (see ADR-007 for how this is handled)
- Debugging requires understanding josh's filter composition syntax