|
| 1 | +<!-- version-type: patch --> |
| 2 | +# service |
| 3 | + |
| 4 | +## ✨ Features |
| 5 | + |
| 6 | +### Patcher Subsystem |
| 7 | + |
| 8 | +Added a patcher that runs one-shot upgrade operations on startup with idempotency and crash recovery: |
| 9 | + |
| 10 | +- `runPatch()` — skips patches that already have a `success` entry in `PatchRun`; retries previously failed or orphaned ones; transitions the run from `running` to `success`/`failed` while capturing timestamped log messages |
| 11 | +- `checkForOrphanedPatch()` — transitions any `PatchRun` rows stuck in `running` (from a previous crash) to `orphaned` so the patch is retried on the next startup |
| 12 | +- `PatchRunStore` — typed data store for `PatchRun` entries with auto-generated ids |
| 13 | +- `setupPatcher()` — wires the patcher into app startup and executes the registered patch list |
| 14 | + |
| 15 | +First shipped patch: `0001-add-stale-status-enum` — migrates legacy `ServiceStatus` rows so the new `'stale'` values are valid for `installStatus` and `buildStatus`. |
| 16 | + |
| 17 | +### External Git Change Detection |
| 18 | + |
| 19 | +`GitHeadWatcher` now uses `chokidar` to watch both `.git/HEAD` and `.git/refs/heads/` for each cloned service (with recursive depth and `awaitWriteFinish` debouncing) and emits a typed `externalChange` event that distinguishes `branch-switched` (HEAD ref changed) from `pull-detected` (branch ref advanced): |
| 20 | + |
| 21 | +```typescript |
| 22 | +type GitHeadChangeEvent = { |
| 23 | + serviceId: string |
| 24 | + previousBranch?: string |
| 25 | + currentBranch?: string |
| 26 | + previousSha?: string |
| 27 | + currentSha?: string |
| 28 | + kind: 'branch-switched' | 'pull-detected' | 'unknown' |
| 29 | +} |
| 30 | +``` |
| 31 | +
|
| 32 | +Added `ExternalGitChangeListener` that subscribes to these events, appends the matching `external-branch-changed` / `external-pull-detected` entry to `ServiceStateHistory`, and downgrades `installStatus`/`buildStatus` from `installed`/`built` to `'stale'` (emitting a `marked-stale` history event). |
| 33 | +
|
| 34 | +### Upstream-Gone Detection |
| 35 | +
|
| 36 | +`GitOperationsService.cloneOrPullService()` now runs `git fetch --prune` before pulling and checks whether `origin/<currentBranch>` still exists. When the upstream is gone it: |
| 37 | +
|
| 38 | +- Sets `ServiceGitStatus.upstreamStatus = 'gone'` |
| 39 | +- Records an `upstream-gone` history event |
| 40 | +- Keeps `cloneStatus = 'cloned'` (the repo is still usable) and returns `{ upstreamGone: true }` |
| 41 | +
|
| 42 | +Pull errors on an already-cloned repo are now surfaced via `ServiceGitStatus.lastPullError` instead of regressing `cloneStatus` to `'failed'`, so the branch selector in the UI stays usable. |
| 43 | +
|
| 44 | +### New Git Actions |
| 45 | +
|
| 46 | +- `ServiceDeleteBranchAction` (`POST /services/:id/delete-branch`) — deletes a local branch; when it is currently checked out, switches to `switchTo` or the remote default branch first, then clears the cached `upstream-gone` marker |
| 47 | +- `ServiceDismissWarningAction` (`POST /services/:id/dismiss-warning`) — records a per-session dismissal flag (`upstreamGone` or `stale`) in the in-memory `ServiceGitStatus` |
| 48 | +
|
| 49 | +### GitService Additions |
| 50 | +
|
| 51 | +Added helper methods used by the above features: |
| 52 | +
|
| 53 | +- `fetch(cwd)` — `git fetch --prune` |
| 54 | +- `hasRemoteBranch(cwd, branch)` — checks for `refs/remotes/origin/<branch>` |
| 55 | +- `getDefaultBranch(cwd)` — resolves `origin/HEAD` |
| 56 | +- `deleteLocalBranch(cwd, branch, force)` — `git branch -d` / `-D` |
| 57 | +- `revParse(cwd, ref)` — returns the SHA of a ref (used to detect pull-advances) |
| 58 | +- `getCommitsBehind(cwd, branch)` — count of commits the local branch is behind origin |
| 59 | +
|
| 60 | +## 🧪 Tests |
| 61 | +
|
| 62 | +- Added `run-patch.spec.ts` and `check-for-orphaned-patch.spec.ts` covering idempotent re-runs, retry of failed patches, and orphan recovery |
| 63 | +- Added `external-git-change-listener.spec.ts` covering history recording and stale-marking for both `branch-switched` and `pull-detected` kinds |
| 64 | +- Added `service-delete-branch-action.spec.ts` covering checkout-before-delete, forced delete, and upstream cleanup |
| 65 | +- Added `service-dismiss-warning-action.spec.ts` covering both warning kinds |
| 66 | +- Expanded `git-head-watcher.spec.ts` for the chokidar-based watcher and the new `externalChange` event shape |
| 67 | +- Expanded `git-operations-service.spec.ts` for upstream-gone detection and the `lastPullError` behavior |
| 68 | +- Expanded `git-service.spec.ts` for `fetch`, `hasRemoteBranch`, `deleteLocalBranch`, and `revParse` |
| 69 | +- Expanded `git-watcher.spec.ts` for the new status fields |
| 70 | +
|
| 71 | +## ⬆️ Dependencies |
| 72 | +
|
| 73 | +- Added `chokidar` `^5.0.0` for reliable recursive filesystem watching on `.git/refs/heads/` |
| 74 | +- Bumped `@furystack/rest-service` from `^12.3.5` to `^13.0.0` |
| 75 | +- Bumped `@furystack/entity-sync-service` from `^1.0.12` to `^1.0.13` |
| 76 | +- Bumped `@furystack/websocket-api` from `^13.2.7` to `^13.2.8` |
| 77 | +- Bumped `vitest` from `^4.1.4` to `^4.1.5` |
0 commit comments