| 42 | 42 | | /// whose commit differs is taken as pushed unless what the push named is a |
| 43 | 43 | | /// commit itself, and is announced at that commit, as `git.push` says its |
| 44 | 44 | | /// `after` is. A tag the store cannot read back is taken as pushed. |
| 45 | | − | pub(crate) async fn pushed_to<R: GitRepo>(git: &R, pushed: &crate::git_http::Pushed) -> Result<Option<String>> { |
| 45 | + | /// |
| 46 | + | /// Artifacts does not read an annotated tag's ref back at all, so the push |
| 47 | + | /// of `cli-v0.3.7` was announced at its tag object (a41bb238) and Actions |
| 48 | + | /// found no workflows there. With the store's ref advertisement |
| 49 | + | /// (`advertised`, refs.rs `all`), a tag is where the advertisement has it |
| 50 | + | /// and is announced at its peeled line (`refs/tags/v1^{}`), the commit. |
| 51 | + | pub(crate) async fn pushed_to<R: GitRepo>( |
| 52 | + | git: &R, |
| 53 | + | pushed: &crate::git_http::Pushed, |
| 54 | + | advertised: Option<&[(String, String)]>, |
| 55 | + | ) -> Result<Option<String>> { |
| 46 | 56 | | if let Some(branch) = pushed.branch() { |
| 47 | 57 | | let head = git.log(branch, 1).await?; |
| 48 | 58 | | return Ok(head.first().filter(|commit| commit.hash == pushed.after).map(|_| pushed.after.clone())); |
| 49 | 59 | | } |
| 60 | + | if let Some(refs) = advertised { |
| 61 | + | let at = |name: &str| refs.iter().find(|(advertised, _)| advertised == name).map(|(_, hash)| hash.clone()); |
| 62 | + | return Ok(match at(&pushed.git_ref) { |
| 63 | + | Some(hash) if hash == pushed.after => Some(at(&format!("{}^{{}}", pushed.git_ref)).unwrap_or(hash)), |
| 64 | + | // Not there, or left elsewhere: the store refused it. |
| 65 | + | _ => None, |
| 66 | + | }); |
| 67 | + | } |
| 50 | 68 | | let Ok(head) = git.log(&pushed.git_ref, 1).await else { |
| 51 | 69 | | return Ok(Some(pushed.after.clone())); |
| 52 | 70 | | }; |
| ⋯ |
| 327 | 345 | | // store reads the tag back as the commit it leads to, main's head. |
| 328 | 346 | | let git = with_refs(&[("main", "m2"), ("refs/tags/cli-v0.3.6", "m2")]); |
| 329 | 347 | | let tag = push("refs/tags/cli-v0.3.6", None, "accae3e8"); |
| 330 | | − | assert_eq!(run(pushed_to(&git, &tag)).unwrap().as_deref(), Some("m2")); |
| 348 | + | assert_eq!(run(pushed_to(&git, &tag, None)).unwrap().as_deref(), Some("m2")); |
| 331 | 349 | | // Moved by a new tag object. |
| 332 | 350 | | let moved = push("refs/tags/cli-v0.3.6", Some("accae3e8"), "b0b0b0b0"); |
| 333 | | − | assert_eq!(run(pushed_to(&git, &moved)).unwrap().as_deref(), Some("m2")); |
| 351 | + | assert_eq!(run(pushed_to(&git, &moved, None)).unwrap().as_deref(), Some("m2")); |
| 352 | + | } |
| 353 | + | |
| 354 | + | fn advertised(refs: &[(&str, &str)]) -> Vec<(String, String)> { |
| 355 | + | refs.iter().map(|&(name, hash)| (name.to_owned(), hash.to_owned())).collect() |
| 356 | + | } |
| 357 | + | |
| 358 | + | #[test] |
| 359 | + | fn an_annotated_tag_the_store_cannot_log_is_announced_at_its_peeled_commit() { |
| 360 | + | // `git push g1t cli-v0.3.7`: Artifacts logs nothing for the tag's |
| 361 | + | // ref, and its advertisement has the tag object and the peeled commit. |
| 362 | + | let git = with_refs(&[("main", "m2")]); |
| 363 | + | let refs = advertised(&[("refs/heads/main", "m2"), ("refs/tags/cli-v0.3.7", "a41bb238"), ("refs/tags/cli-v0.3.7^{}", "m1")]); |
| 364 | + | let tag = push("refs/tags/cli-v0.3.7", None, "a41bb238"); |
| 365 | + | assert_eq!(run(pushed_to(&git, &tag, Some(&refs))).unwrap().as_deref(), Some("m1")); |
| 366 | + | // A lightweight tag has no peeled line: it is its commit. |
| 367 | + | let refs = advertised(&[("refs/tags/v1", "f2")]); |
| 368 | + | assert_eq!(run(pushed_to(&git, &push("refs/tags/v1", None, "f2"), Some(&refs))).unwrap().as_deref(), Some("f2")); |
| 369 | + | // Left elsewhere, or not there at all: refused. |
| 370 | + | assert_eq!(run(pushed_to(&git, &push("refs/tags/v1", Some("f2"), "m1"), Some(&refs))).unwrap(), None); |
| 371 | + | assert_eq!(run(pushed_to(&git, &push("refs/tags/v2", None, "m1"), Some(&refs))).unwrap(), None); |
| 334 | 372 | | } |
| 335 | 373 | | |
| 336 | 374 | | #[test] |
| 337 | 375 | | fn a_lightweight_tag_is_announced_where_the_store_has_it() { |
| 338 | 376 | | let git = with_refs(&[("refs/tags/v1", "f2")]); |
| 339 | | − | assert_eq!(run(pushed_to(&git, &push("refs/tags/v1", None, "f2"))).unwrap().as_deref(), Some("f2")); |
| 377 | + | assert_eq!(run(pushed_to(&git, &push("refs/tags/v1", None, "f2"), None)).unwrap().as_deref(), Some("f2")); |
| 340 | 378 | | // The push named a commit the store did not move the tag to. |
| 341 | | − | assert_eq!(run(pushed_to(&git, &push("refs/tags/v1", Some("f2"), "m1"))).unwrap(), None); |
| 379 | + | assert_eq!(run(pushed_to(&git, &push("refs/tags/v1", Some("f2"), "m1"), None)).unwrap(), None); |
| 342 | 380 | | // A tag the store cannot read back is taken as pushed. |
| 343 | | − | assert_eq!(run(pushed_to(&git, &push("refs/tags/v2", None, "m1"))).unwrap().as_deref(), Some("m1")); |
| 381 | + | assert_eq!(run(pushed_to(&git, &push("refs/tags/v2", None, "m1"), None)).unwrap().as_deref(), Some("m1")); |
| 344 | 382 | | } |
| 345 | 383 | | |
| 346 | 384 | | #[test] |
| 347 | 385 | | fn a_branch_is_announced_only_at_the_commit_pushed() { |
| 348 | 386 | | let git = with_refs(&[("main", "m2")]); |
| 349 | | − | assert_eq!(run(pushed_to(&git, &push("refs/heads/main", Some("m1"), "m2"))).unwrap().as_deref(), Some("m2")); |
| 350 | | − | assert_eq!(run(pushed_to(&git, &push("refs/heads/main", Some("m2"), "f2"))).unwrap(), None); |
| 351 | | − | assert_eq!(run(pushed_to(&git, &push("refs/heads/gone", None, "f2"))).unwrap(), None); |
| 387 | + | assert_eq!(run(pushed_to(&git, &push("refs/heads/main", Some("m1"), "m2"), None)).unwrap().as_deref(), Some("m2")); |
| 388 | + | assert_eq!(run(pushed_to(&git, &push("refs/heads/main", Some("m2"), "f2"), None)).unwrap(), None); |
| 389 | + | assert_eq!(run(pushed_to(&git, &push("refs/heads/gone", None, "f2"), None)).unwrap(), None); |
| 352 | 390 | | } |
| 353 | 391 | | } |