Files
josh-sync/docs/adr/006-inline-exclude-filter.md
Slim B 8ab07b83ab Update docs, changelog, examples, and add ADRs for v1.2
- 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>
2026-02-14 21:28:40 +03:00

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

  1. .josh-sync-exclude file 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).

  2. Post-clone file deletion: Clone through josh, then rm -rf excluded paths before pushing. Fragile — deletions create diff noise. Doesn't work for reverse sync (excluded files would appear as "deleted" in the subrepo).

  3. Josh :exclude filter 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).

  4. 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 (skip detection) works correctly since excluded files aren't in the filtered view
  • Standard josh syntax — no custom invention

Negative:

  • Josh's :exclude pattern 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