Files
josh-sync/docs/adr/006-inline-exclude-filter.md
sync-bot b35703d271 Import josh-sync from subrepo/main
Sync-Origin: import/josh-sync/20260528-041502
2026-05-28 04:15:02 +01: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