tests: SSE parsing + relay event handling, update AGENTS.md
- New test module test/RelaySpec.hs (20 tests):
- extractLines: line splitting, CRLF, partial lines, empty input
- foldLines: single/two push events, non-push ignored, comments, no-space colon,
partial state carry-over across batches
- parseEvent: valid push, non-push, malformed JSON, multi-line data
- handleSSEEvent: pull on matching repo, ignore wrong gitea_repo/branch,
skip pull when merge/rebase/operation in progress
- Exported PushEvent(..), extractLines, foldLines, parseEvent, handleSSEEvent
- Fixed data line order (append instead of prepend+reverse) and event ordering
- Updated AGENTS.md: relay architecture, Go component, new deps, file layout
This commit is contained in:
@@ -34,6 +34,25 @@ All core logic lives in the single `Converge` module (`src/Converge.hs`). Key se
|
||||
- **Sync cycle** (`syncRepo`): guarded by `isInMiddleOfOperation`, then stage + commit + pull + conflict check
|
||||
- **Git operations**: shell out to `git` via `System.Process.readProcessWithExitCode` — no libgit2 dependency
|
||||
- **Notifications**: shell out to `notify-send` via `System.Process.spawnProcess`
|
||||
- **Relay listener**: `connectRelay` connects to a converge-relay SSE endpoint, parses push events, and triggers pulls on matching repos (by `gitea_repo` + branch). Runs as a background thread with exponential backoff reconnection.
|
||||
|
||||
### Relay architecture
|
||||
|
||||
```
|
||||
┌─────────────┐ POST /webhook ┌──────────────────┐ GET /events (SSE) ┌──────────────────┐
|
||||
│ Gitea │ ────────────────> │ converge-relay │ ───────────────────> │ converge (local) │
|
||||
│ Server │ (push event) │ (Go, hosted) │ event: push │ (Haskell) │
|
||||
└─────────────┘ └──────────────────┘ └─────────┬─────────┘
|
||||
│
|
||||
matches gitea_repo
|
||||
→ git pull
|
||||
```
|
||||
|
||||
The Go relay is a zero-dependency pub/sub server. Key design:
|
||||
- `POST /webhook` — receives Gitea push events, optional HMAC-SHA256 verification
|
||||
- `GET /events` — SSE stream, clients connect and hold the connection open
|
||||
- Channel-based broker: `Subscribe()` → channel, `Unsubscribe(ch)`, `Publish(event)` — non-blocking send to all subscribers
|
||||
- Slow subscribers are silently dropped (channel buffer = 16)
|
||||
|
||||
### Git operations
|
||||
|
||||
@@ -62,6 +81,8 @@ All core logic lives in the single `Converge` module (`src/Converge.hs`). Key se
|
||||
|
||||
## Build / test / run
|
||||
|
||||
### Haskell (converge)
|
||||
|
||||
```bash
|
||||
stack build # Build library + executable + tests
|
||||
stack test # Build and run the test suite
|
||||
@@ -71,6 +92,15 @@ stack exec converge -- [options] # Run the tool directly
|
||||
./scripts/run # Run the tool (uses Docker via ./hs)
|
||||
```
|
||||
|
||||
### Go (converge-relay)
|
||||
|
||||
```bash
|
||||
cd converge-relay
|
||||
./scripts/build # Format + test + build (uses Docker via ./goctl)
|
||||
./scripts/test # Run tests (uses Docker via ./goctl)
|
||||
./scripts/run --listen :8080 # Run the relay (uses Docker via ./goctl)
|
||||
```
|
||||
|
||||
## Test conventions
|
||||
|
||||
Tests use **hspec** and create real temporary git repositories — no mocking of git commands. This avoids complexity and tests actual git semantics.
|
||||
@@ -105,6 +135,7 @@ Anytime new behavior is added the `SPEC.md` should be updated and a correspondin
|
||||
| Pulls from remote origin | `GitPullSpec` | Fast-forward pull scenario |
|
||||
| Rebases on pull | `GitPullSpec` | Divergent history → linear history, no merge commits |
|
||||
| No-op during operations | `GitSafetySpec` | All 7 sentinel types + clean repo |
|
||||
| `RelaySpec` | SSE line parsing, event folding, JSON parsing, repo matching, operation-in-progress skip |
|
||||
|
||||
## Key conventions
|
||||
|
||||
@@ -117,17 +148,29 @@ Anytime new behavior is added the `SPEC.md` should be updated and a correspondin
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Haskell
|
||||
|
||||
| Package | Purpose |
|
||||
|---|---|
|
||||
| `async` | Concurrent repo watching |
|
||||
| `fsnotify` | File system event monitoring |
|
||||
| `optparse-applicative` | CLI argument parsing |
|
||||
| `yaml` | Config file parsing |
|
||||
| `http-client` | HTTP client for SSE relay connection |
|
||||
| `http-client-tls` | TLS support for HTTPS relay endpoints |
|
||||
| `http-types` | HTTP status codes |
|
||||
| `aeson` | JSON parsing (push events from relay) |
|
||||
| `bytestring` | Efficient binary data for SSE stream parsing |
|
||||
| `temporary` (test only) | Temp directories for test repos |
|
||||
| `hspec` (test only) | Test framework |
|
||||
|
||||
### Go (converge-relay)
|
||||
|
||||
Zero external dependencies — stdlib only (`net/http`, `log/slog`, `crypto/hmac`, `encoding/json`, `sync`).
|
||||
|
||||
## External requirements
|
||||
|
||||
- `git` must be on PATH (no libgit2 dependency)
|
||||
- `notify-send` for desktop notifications (optional — only called on conflict)
|
||||
- `notify-send` for desktop notifications (optional — only called on conflict/pull)
|
||||
- `hostname` command for commit message generation
|
||||
- For realtime pull: a running `converge-relay` instance reachable via HTTP(S)
|
||||
|
||||
Reference in New Issue
Block a user