Diffusion: a bump's pipeline runs f01cb865fc60
A hub-made bump does not come through the push path, so its pipeline did not start and the proposal went untested. The hub now starts the consumer's pipeline for the bump bookmark as a push of it would. refs #148
- Author
- Andy McCall @andymccall <[email protected]>
- When
- · 2026-10-13 02:07 UTC
- Signature
- ✔ signed by Andy McCall
- Hash
f01cb865fc60230b8370579a98b7eac42de01abe2ac040d85bbd0acf03f9e80d- Depends on
- 1d634f698ecd 3776b70d9b17 42fa95540468 a7e6e8e6094c f677d6270298
- Depended on by
- f4d6ef418a96 c4e432da4bc9 9aaa88b2d9c8
- In bookmarks
- api-unknown-fields cancel-landed-runs close-proposal-140 diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
- In tags
- v0.5.0 v0.5.1 v0.5.2 v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
- Edits
- 8 atoms, 0 tree operations, 2 files
Browse the tree at this change
modified crates/hub/src/diffusion.rs +19 −3
| … | 175 unchanged lines |
||
| 176 | 176 | #[derive(Clone, Debug, PartialEq, Eq)] |
|
| 177 | 177 | pub enum Bumped { |
|
| 178 | 178 | /// The bump bookmark now holds the target plus one change moving the pin. |
|
| 179 | - | Moved { bookmark: String, change: ChangeHash, heads: Vec<ChangeHash>, from: Vec<ChangeHash> }, |
|
| 179 | + | Moved { bookmark: String, change: ChangeHash, heads: Vec<ChangeHash>, from: Vec<ChangeHash>, state: String }, |
|
| 180 | 180 | /// The pin already holds the upstream state. |
|
| 181 | 181 | UpToDate, |
|
| 182 | 182 | /// The bump bookmark already moves the pin to that state. |
|
| … | 106 unchanged lines |
||
| 289 | 289 | // target each time, so the bump it replaces may go. |
|
| 290 | 290 | let mut update = r.host_ref_update(&bookmark, &heads, Some((&key, &secret)))?; |
|
| 291 | 291 | update.force = true; |
|
| 292 | + | let state = update.new_state.to_hex(); |
|
| 292 | 293 | let report = r.apply_hosted(Some((change, vec![sig])), &[identity], update)?; |
|
| 293 | 294 | if let Some(refused) = report.results.iter().find(|x| !x.ok) { |
|
| 294 | 295 | return Err(crate::repos::RepoError::Oxy(oxy_core::repo::RepoError::Msg(format!( |
|
| … | 1 unchanged lines |
||
| 296 | 297 | refused.reason |
|
| 297 | 298 | )))); |
|
| 298 | 299 | } |
|
| 299 | - | Ok(Bumped::Moved { bookmark, change: hash, heads, from: old }) |
|
| 300 | + | Ok(Bumped::Moved { bookmark, change: hash, heads, from: old, state }) |
|
| 300 | 301 | }) |
|
| 301 | 302 | .await |
|
| 302 | 303 | } |
|
| … | 109 unchanged lines |
||
| 412 | 413 | Some(number) |
|
| 413 | 414 | } |
|
| 414 | 415 | ||
| 416 | + | /// Start a bump's pipeline, as a push of it would. Boxed as `Send`, which |
|
| 417 | + | /// breaks the cycle between a bump starting runs and a passing run |
|
| 418 | + | /// starting bumps that the compiler cannot see through. |
|
| 419 | + | fn start_runs( |
|
| 420 | + | app: Shared, |
|
| 421 | + | row: RepoRow, |
|
| 422 | + | bookmark: String, |
|
| 423 | + | state: String, |
|
| 424 | + | heads: Vec<ChangeHash>, |
|
| 425 | + | ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>> { |
|
| 426 | + | Box::pin(async move { crate::flow::on_push(&app, &row, &bookmark, &state, &heads, ACCOUNT).await }) |
|
| 427 | + | } |
|
| 428 | + | ||
| 415 | 429 | /// The text of `.oxymodules` on `bookmark` of `name`, if any. |
|
| 416 | 430 | async fn oxymodules(app: &Shared, name: &RepoName, bookmark: &str) -> Option<String> { |
|
| 417 | 431 | let b = bookmark.to_string(); |
|
| … | 52 unchanged lines |
||
| 470 | 484 | continue; |
|
| 471 | 485 | } |
|
| 472 | 486 | match bump(app, &consumer, &target, &path, upstream, heads.clone()).await { |
|
| 473 | - | Ok(Bumped::Moved { from, .. }) => { |
|
| 487 | + | Ok(Bumped::Moved { from, bookmark, heads: bumped, state, .. }) => { |
|
| 488 | + | // Its pipeline runs as for a push, so the proposal is tested. |
|
| 489 | + | tokio::spawn(start_runs(app.clone(), consumer.clone(), bookmark, state, bumped)); |
|
| 474 | 490 | let n = propose(app, &consumer, &target, &path, upstream, from, heads.clone()).await; |
|
| 475 | 491 | tracing::info!( |
|
| 476 | 492 | "diffusion: {}/{} {path} bumped to {}/{} (proposal {n:?})", |
|
| … | 204 unchanged lines |
modified crates/hub/tests/flow.rs +13 −0
| … | 3277 unchanged lines |
||
| 3278 | 3278 | let game = client(&format!("{}/andy/game", h.base), game_work.path()); |
|
| 3279 | 3279 | game.submodule_add(&format!("{}/andy/api", h.base), "deps/api", Some("main")).unwrap(); |
|
| 3280 | 3280 | write(game_work.path(), "game.c", "int main;\n"); |
|
| 3281 | + | write(game_work.path(), ".flow/pipeline.kdl", &pipeline("")); |
|
| 3282 | + | write(game_work.path(), "test.sh", "echo ok\n"); |
|
| 3281 | 3283 | game.snapshot("t").unwrap(); |
|
| 3282 | 3284 | game.commit("game on api v1", &[]).unwrap(); |
|
| 3283 | 3285 | assert!(game.push("origin", false).unwrap().iter().all(|r| r.ok)); |
|
| … | 24 unchanged lines |
||
| 3308 | 3310 | assert_eq!(put(r#"{"follow":"any"}"#).await.0, StatusCode::OK); |
|
| 3309 | 3311 | let first = bump().await.expect("caught up when turned on"); |
|
| 3310 | 3312 | assert!(first.title.contains("1 change") && first.body.contains("api v2: faster sprites"), "{:?}", first.title); |
|
| 3313 | + | // The bump's pipeline runs, as a push of it would, so the proposal is tested. |
|
| 3314 | + | let mut ran = false; |
|
| 3315 | + | for _ in 0..100 { |
|
| 3316 | + | if runs_of(&h.app, "andy", "game").await.iter().any(|r| r.bookmark == "diffusion/deps/api") { |
|
| 3317 | + | ran = true; |
|
| 3318 | + | break; |
|
| 3319 | + | } |
|
| 3320 | + | tokio::time::sleep(std::time::Duration::from_millis(50)).await; |
|
| 3321 | + | } |
|
| 3322 | + | assert!(ran, "a run of the bump"); |
|
| 3323 | + | pass_all().await; |
|
| 3311 | 3324 | write(api_work.path(), "a3.c", "v3\n"); |
|
| 3312 | 3325 | api.snapshot("t").unwrap(); |
|
| 3313 | 3326 | api.commit("api v3: save slots past ten", &[]).unwrap(); |
|
| … | 92 unchanged lines |