Move push flow into SyncState methods with step callbacks

This commit is contained in:
2026-10-13 19:14:32 +07:00
parent 5b16ba50c2
commit 62f06fc52b
4 changed files with 293 additions and 25 deletions
+9 -9
View File
@@ -37,7 +37,8 @@ Mechanical, no behavior change.
Replace `push_staged_to_grasps` with methods on the type that owns the refs:
```rust
#[derive(Debug, Clone, PartialEq, Eq, Default)]
/// How much to push to the grasp servers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GitPushMode {
/// Mirror all branches and tags.
#[default]
@@ -106,7 +107,7 @@ impl SyncState {
// via cx.background_spawn (blocking gix work). Reports every transition
// through on_step. Ends with the relay fan-out broadcast. Errors once
// up front when self.path is None (state not built from a local repo).
pub(crate) async fn push_to_grasps<P>(
pub async fn push_to_grasps<P>(
&self,
cx: &mut AsyncApp,
client: &Client,
@@ -118,11 +119,10 @@ impl SyncState {
P: FnMut(PushStepStatus, &mut AsyncApp) -> Result<(), Error>,
{ ... }
// announce_repository_and_push moved as-is: sign + broadcast the
// announcement (AnnounceRepository step), parse it into an
// `Announcement`, sign the state event, delegate to push_to_grasps,
// retract on total failure. This one keeps the signer.
pub(crate) async fn announce_and_push<S, P>(...) -> Result<Event, Error> { ... }
// Announces the pre-signed announcement (AnnounceRepository step), then
// delegates to push_to_grasps with the pre-signed state event. No signer:
// on failure the call site retracts the announcement by id.
pub async fn announce_and_push<P>(...) -> Result<Event, Error> { ... }
}
```
@@ -132,7 +132,7 @@ The struct carries the job description — what (`refs`, `head`), from where (`p
- `refs`, `head`, `path`, `mode` come from `&self`. The `push: fn(&Path, &str, &str, &str) -> Result<(), Error>` callback becomes the `mode` field, resolved inside to `Repo::push_main` / `Repo::push_all`; the wildcard-refspec mirror semantics of `push_all` are preserved. States built by `SyncState::parse` (remote events) have no local repo, so `path` is `Option<PathBuf>` and `push_to_grasps` errors once up front when it is `None`.
- `repo_id`, `owner`, `servers` collapse into `&Announcement` — its `id`, `owner` and `relays` are exactly those values, and both call sites already hold one. `push_repo_from` currently destructures the announcement into those three fields just to re-pass them. It stays an argument because states parsed from events carry no announcement.
- The state event is signed by the caller via `state_event(..).finalize_async(&signer)`, so `push_to_grasps` takes a signed `Event` and needs no signer or `S` generic — it only ever sends events. The signer lives with the callers (`push_repo_from`, `announce_and_push`, which also signs the announcement and the retraction).
- The state event is signed by the caller via `state_event(..).finalize_async(&signer)`, so `push_to_grasps` takes a signed `Event` and needs no signer or `S` generic — it only ever sends events. The signer lives entirely with the callers: `announce_and_push` takes the pre-signed announcement and state events, so a failing call site can retract the announcement by id.
- `client` stays as a reference per the constraints; `SyncState` remains plain data, so `SyncState::parse` keeps working for remote states that have no client or path.
- `on_step` takes `&mut AsyncApp` so it can update an entity directly. That is what lets the whole flow run in the caller's foreground `cx.spawn` task and emit progress without a channel.
@@ -147,7 +147,7 @@ The struct carries the job description — what (`refs`, `head`), from where (`p
- Add `BackendEvent::PushProgress { addr: RepoAddr, status: PushStepStatus }`.
- `push_repo_from`: background-spawn `SyncState::from_repo(&path)`, sign `state.state_event(&announcement.id)` with the signer, then call `push_to_grasps(cx, &client, event, &announcement, on_step)` in the same foreground task, `on_step` emitting the event via `this.update`. The nested `background_spawn` and its clone block disappear. Keep the `refs.is_empty()` early return at the call site.
- `announce_repository_and_push` is deleted from `backend.rs`; its callers build a `SyncState` first (`SyncState::from_repo` for publish; repo creation builds the single-ref state literally with `GitPushMode::Main`), then call `announce_and_push(cx, client, signer, announcement, on_step)`.
- `announce_repository_and_push` is deleted from `backend.rs`; its callers build a `SyncState` first (`SyncState::from_repo` for publish; repo creation builds the single-ref state literally with `GitPushMode::Main`), sign the announcement and state events, then call `announce_and_push(client, announcement, state_event, cx, on_step)`. When it fails, the call site retracts via `SyncState::retract_announcement` — retraction is the very end of the flow, not inside the method.
- `push.rs` keeps `GraspServer`, `PushOutcome` and the URL helpers.
## Step 4 — UI checklist