Files
josh-sync/docs/adr/006-inline-exclude-filter.md

56 lines
2.7 KiB
Markdown
Raw Permalink Normal View History

# 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:
```yaml
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