diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-08-18 08:04:49 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-08-18 08:04:49 +0100 |
| commit | 948c8595acea9a783a38002371c40185868ce923 (patch) | |
| tree | 10dce3b3dc487e5d27aee706d64738bd72f1e2ce | |
| parent | 5618fd9883d45de1443a40abada944cbe3bb8dfd (diff) | |
feat(remote): `push` publish merge event
when a merge commit is being pushed that merges a patch in a proposal
| -rw-r--r-- | src/git.rs | 2 | ||||
| -rw-r--r-- | src/git_remote_helper.rs | 230 | ||||
| -rw-r--r-- | tests/git_remote_helper.rs | 152 |
3 files changed, 378 insertions, 6 deletions
| @@ -787,7 +787,7 @@ pub fn oid_to_sha1(oid: &Oid) -> Sha1Hash { | |||
| 787 | } | 787 | } |
| 788 | 788 | ||
| 789 | /// `Sha1Hash` to git2 `Oid` object | 789 | /// `Sha1Hash` to git2 `Oid` object |
| 790 | fn sha1_to_oid(hash: &Sha1Hash) -> Result<Oid> { | 790 | pub fn sha1_to_oid(hash: &Sha1Hash) -> Result<Oid> { |
| 791 | Oid::from_bytes(hash.as_byte_array()).context("Sha1Hash bytes failed to produce a valid Oid") | 791 | Oid::from_bytes(hash.as_byte_array()).context("Sha1Hash bytes failed to produce a valid Oid") |
| 792 | } | 792 | } |
| 793 | 793 | ||
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs index 97db69a..a03c6cf 100644 --- a/src/git_remote_helper.rs +++ b/src/git_remote_helper.rs | |||
| @@ -18,9 +18,9 @@ use client::{ | |||
| 18 | consolidate_fetch_reports, get_events_from_cache, get_repo_ref_from_cache, | 18 | consolidate_fetch_reports, get_events_from_cache, get_repo_ref_from_cache, |
| 19 | get_state_from_cache, sign_event, Connect, STATE_KIND, | 19 | get_state_from_cache, sign_event, Connect, STATE_KIND, |
| 20 | }; | 20 | }; |
| 21 | use git::RepoActions; | 21 | use git::{sha1_to_oid, RepoActions}; |
| 22 | use git2::{Oid, Repository}; | 22 | use git2::{Oid, Repository}; |
| 23 | use nostr::nips::nip01::Coordinate; | 23 | use nostr::nips::{nip01::Coordinate, nip10::Marker}; |
| 24 | use nostr_sdk::{ | 24 | use nostr_sdk::{ |
| 25 | hashes::sha1::Hash as Sha1Hash, Event, EventBuilder, EventId, Kind, PublicKey, Tag, Url, | 25 | hashes::sha1::Hash as Sha1Hash, Event, EventBuilder, EventId, Kind, PublicKey, Tag, Url, |
| 26 | }; | 26 | }; |
| @@ -713,6 +713,18 @@ async fn push( | |||
| 713 | RepoState::build(repo_ref.identifier.clone(), new_state, &signer).await?; | 713 | RepoState::build(repo_ref.identifier.clone(), new_state, &signer).await?; |
| 714 | 714 | ||
| 715 | events.push(new_repo_state.event); | 715 | events.push(new_repo_state.event); |
| 716 | |||
| 717 | for event in get_merged_status_events( | ||
| 718 | repo_ref, | ||
| 719 | git_repo, | ||
| 720 | nostr_remote_url, | ||
| 721 | &signer, | ||
| 722 | &git_server_refspecs, | ||
| 723 | ) | ||
| 724 | .await? | ||
| 725 | { | ||
| 726 | events.push(event); | ||
| 727 | } | ||
| 716 | } | 728 | } |
| 717 | 729 | ||
| 718 | let mut rejected_proposal_refspecs = vec![]; | 730 | let mut rejected_proposal_refspecs = vec![]; |
| @@ -1115,6 +1127,220 @@ fn generate_updated_state( | |||
| 1115 | Ok(new_state) | 1127 | Ok(new_state) |
| 1116 | } | 1128 | } |
| 1117 | 1129 | ||
| 1130 | async fn get_merged_status_events( | ||
| 1131 | repo_ref: &RepoRef, | ||
| 1132 | git_repo: &Repo, | ||
| 1133 | remote_nostr_url: &str, | ||
| 1134 | signer: &NostrSigner, | ||
| 1135 | refspecs_to_git_server: &Vec<String>, | ||
| 1136 | ) -> Result<Vec<Event>> { | ||
| 1137 | let mut events = vec![]; | ||
| 1138 | for refspec in refspecs_to_git_server { | ||
| 1139 | let (from, to) = refspec_to_from_to(refspec)?; | ||
| 1140 | if to.eq("refs/heads/main") || to.eq("refs/heads/master") { | ||
| 1141 | let tip_of_pushed_branch = git_repo.get_commit_or_tip_of_reference(from)?; | ||
| 1142 | let Ok(tip_of_remote_branch) = git_repo.get_commit_or_tip_of_reference( | ||
| 1143 | &refspec_remote_ref_name(&git_repo.git_repo, refspec, remote_nostr_url)?, | ||
| 1144 | ) else { | ||
| 1145 | // branch not on remote | ||
| 1146 | continue; | ||
| 1147 | }; | ||
| 1148 | let (ahead, _) = | ||
| 1149 | git_repo.get_commits_ahead_behind(&tip_of_remote_branch, &tip_of_pushed_branch)?; | ||
| 1150 | for commit_hash in ahead { | ||
| 1151 | let commit = git_repo.git_repo.find_commit(sha1_to_oid(&commit_hash)?)?; | ||
| 1152 | if commit.parent_count() > 1 { | ||
| 1153 | // merge commit | ||
| 1154 | for parent in commit.parents() { | ||
| 1155 | // lookup parent id | ||
| 1156 | let commit_events = get_events_from_cache( | ||
| 1157 | git_repo.get_path()?, | ||
| 1158 | vec![ | ||
| 1159 | nostr::Filter::default() | ||
| 1160 | .kind(nostr::Kind::GitPatch) | ||
| 1161 | .reference(parent.id().to_string()), | ||
| 1162 | ], | ||
| 1163 | ) | ||
| 1164 | .await?; | ||
| 1165 | if let Some(commit_event) = commit_events.iter().find(|e| { | ||
| 1166 | e.tags.iter().any(|t| { | ||
| 1167 | t.as_vec()[0].eq("commit") | ||
| 1168 | && t.as_vec()[1].eq(&parent.id().to_string()) | ||
| 1169 | }) | ||
| 1170 | }) { | ||
| 1171 | let (proposal_id, revision_id) = | ||
| 1172 | get_proposal_and_revision_root_from_patch(git_repo, commit_event) | ||
| 1173 | .await?; | ||
| 1174 | // TODO: write to terminal to tell user | ||
| 1175 | events.push( | ||
| 1176 | create_merge_status( | ||
| 1177 | signer, | ||
| 1178 | repo_ref, | ||
| 1179 | &get_event_from_cache_by_id(git_repo, &proposal_id).await?, | ||
| 1180 | &if let Some(revision_id) = revision_id { | ||
| 1181 | Some( | ||
| 1182 | get_event_from_cache_by_id(git_repo, &revision_id) | ||
| 1183 | .await?, | ||
| 1184 | ) | ||
| 1185 | } else { | ||
| 1186 | None | ||
| 1187 | }, | ||
| 1188 | &commit_hash, | ||
| 1189 | commit_event.id(), | ||
| 1190 | ) | ||
| 1191 | .await?, | ||
| 1192 | ); | ||
| 1193 | } | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | Ok(events) | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | async fn get_event_from_cache_by_id(git_repo: &Repo, event_id: &EventId) -> Result<Event> { | ||
| 1203 | Ok(get_events_from_cache( | ||
| 1204 | git_repo.get_path()?, | ||
| 1205 | vec![nostr::Filter::default().id(*event_id)], | ||
| 1206 | ) | ||
| 1207 | .await? | ||
| 1208 | .first() | ||
| 1209 | .context("cannot find event in cache")? | ||
| 1210 | .clone()) | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | async fn create_merge_status( | ||
| 1214 | signer: &NostrSigner, | ||
| 1215 | repo_ref: &RepoRef, | ||
| 1216 | proposal: &Event, | ||
| 1217 | revision: &Option<Event>, | ||
| 1218 | merge_commit: &Sha1Hash, | ||
| 1219 | merged_patch: EventId, | ||
| 1220 | ) -> Result<Event> { | ||
| 1221 | let mut public_keys = repo_ref | ||
| 1222 | .maintainers | ||
| 1223 | .iter() | ||
| 1224 | .copied() | ||
| 1225 | .collect::<HashSet<PublicKey>>(); | ||
| 1226 | public_keys.insert(proposal.author()); | ||
| 1227 | if let Some(revision) = revision { | ||
| 1228 | public_keys.insert(revision.author()); | ||
| 1229 | } | ||
| 1230 | sign_event( | ||
| 1231 | EventBuilder::new( | ||
| 1232 | nostr::event::Kind::GitStatusApplied, | ||
| 1233 | String::new(), | ||
| 1234 | [ | ||
| 1235 | vec![ | ||
| 1236 | Tag::custom( | ||
| 1237 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")), | ||
| 1238 | vec!["git proposal merged / applied".to_string()], | ||
| 1239 | ), | ||
| 1240 | Tag::from_standardized(nostr::TagStandard::Event { | ||
| 1241 | event_id: proposal.id(), | ||
| 1242 | relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::new), | ||
| 1243 | marker: Some(Marker::Root), | ||
| 1244 | public_key: None, | ||
| 1245 | }), | ||
| 1246 | Tag::from_standardized(nostr::TagStandard::Event { | ||
| 1247 | event_id: merged_patch, | ||
| 1248 | relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::new), | ||
| 1249 | marker: Some(Marker::Mention), | ||
| 1250 | public_key: None, | ||
| 1251 | }), | ||
| 1252 | ], | ||
| 1253 | if let Some(revision) = revision { | ||
| 1254 | vec![Tag::from_standardized(nostr::TagStandard::Event { | ||
| 1255 | event_id: revision.id(), | ||
| 1256 | relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::new), | ||
| 1257 | marker: Some(Marker::Root), | ||
| 1258 | public_key: None, | ||
| 1259 | })] | ||
| 1260 | } else { | ||
| 1261 | vec![] | ||
| 1262 | }, | ||
| 1263 | public_keys.iter().map(|pk| Tag::public_key(*pk)).collect(), | ||
| 1264 | repo_ref | ||
| 1265 | .coordinates() | ||
| 1266 | .iter() | ||
| 1267 | .map(|c| Tag::coordinate(c.clone())) | ||
| 1268 | .collect::<Vec<Tag>>(), | ||
| 1269 | vec![ | ||
| 1270 | Tag::from_standardized(nostr::TagStandard::Reference( | ||
| 1271 | repo_ref.root_commit.to_string(), | ||
| 1272 | )), | ||
| 1273 | Tag::from_standardized(nostr::TagStandard::Reference(format!( | ||
| 1274 | "{merge_commit}" | ||
| 1275 | ))), | ||
| 1276 | Tag::custom( | ||
| 1277 | nostr::TagKind::Custom(std::borrow::Cow::Borrowed("merge-commit-id")), | ||
| 1278 | vec![format!("{merge_commit}")], | ||
| 1279 | ), | ||
| 1280 | ], | ||
| 1281 | ] | ||
| 1282 | .concat(), | ||
| 1283 | ), | ||
| 1284 | signer, | ||
| 1285 | ) | ||
| 1286 | .await | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | async fn get_proposal_and_revision_root_from_patch( | ||
| 1290 | git_repo: &Repo, | ||
| 1291 | patch: &Event, | ||
| 1292 | ) -> Result<(EventId, Option<EventId>)> { | ||
| 1293 | let proposal_or_revision = if patch.tags.iter().any(|t| t.as_vec()[1].eq("root")) { | ||
| 1294 | patch.clone() | ||
| 1295 | } else { | ||
| 1296 | let proposal_or_revision_id = EventId::parse( | ||
| 1297 | if let Some(t) = patch.tags.iter().find(|t| t.is_root()) { | ||
| 1298 | t.clone() | ||
| 1299 | } else if let Some(t) = patch.tags.iter().find(|t| t.is_reply()) { | ||
| 1300 | t.clone() | ||
| 1301 | } else { | ||
| 1302 | Tag::event(patch.id()) | ||
| 1303 | } | ||
| 1304 | .as_vec()[1] | ||
| 1305 | .clone(), | ||
| 1306 | )?; | ||
| 1307 | |||
| 1308 | get_events_from_cache( | ||
| 1309 | git_repo.get_path()?, | ||
| 1310 | vec![nostr::Filter::default().id(proposal_or_revision_id)], | ||
| 1311 | ) | ||
| 1312 | .await? | ||
| 1313 | .first() | ||
| 1314 | .unwrap() | ||
| 1315 | .clone() | ||
| 1316 | }; | ||
| 1317 | |||
| 1318 | if !proposal_or_revision.kind().eq(&Kind::GitPatch) { | ||
| 1319 | bail!("thread root is not a git patch"); | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | if proposal_or_revision | ||
| 1323 | .tags | ||
| 1324 | .iter() | ||
| 1325 | .any(|t| t.as_vec()[1].eq("revision-root")) | ||
| 1326 | { | ||
| 1327 | Ok(( | ||
| 1328 | EventId::parse( | ||
| 1329 | proposal_or_revision | ||
| 1330 | .tags | ||
| 1331 | .iter() | ||
| 1332 | .find(|t| t.is_reply()) | ||
| 1333 | .unwrap() | ||
| 1334 | .as_vec()[1] | ||
| 1335 | .clone(), | ||
| 1336 | )?, | ||
| 1337 | Some(proposal_or_revision.id()), | ||
| 1338 | )) | ||
| 1339 | } else { | ||
| 1340 | Ok((proposal_or_revision.id(), None)) | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | |||
| 1118 | fn update_remote_refs_pushed( | 1344 | fn update_remote_refs_pushed( |
| 1119 | git_repo: &Repository, | 1345 | git_repo: &Repository, |
| 1120 | refspec: &str, | 1346 | refspec: &str, |
diff --git a/tests/git_remote_helper.rs b/tests/git_remote_helper.rs index 17138e4..98637e8 100644 --- a/tests/git_remote_helper.rs +++ b/tests/git_remote_helper.rs | |||
| @@ -2,8 +2,9 @@ use std::{collections::HashSet, env::current_dir}; | |||
| 2 | 2 | ||
| 3 | use anyhow::{Context, Result}; | 3 | use anyhow::{Context, Result}; |
| 4 | use futures::join; | 4 | use futures::join; |
| 5 | use git2::Oid; | ||
| 5 | use nostr::nips::nip01::Coordinate; | 6 | use nostr::nips::nip01::Coordinate; |
| 6 | use nostr_sdk::{secp256k1::rand, Kind, ToBech32}; | 7 | use nostr_sdk::{secp256k1::rand, Event, JsonUtil, Kind, ToBech32}; |
| 7 | use relay::Relay; | 8 | use relay::Relay; |
| 8 | use serial_test::serial; | 9 | use serial_test::serial; |
| 9 | use test_utils::{git::GitTestRepo, *}; | 10 | use test_utils::{git::GitTestRepo, *}; |
| @@ -41,6 +42,9 @@ fn set_git_nostr_login_config(test_repo: &GitTestRepo) -> Result<()> { | |||
| 41 | .context("cannot open git config")?; | 42 | .context("cannot open git config")?; |
| 42 | config.set_str("nostr.nsec", TEST_KEY_2_NSEC)?; | 43 | config.set_str("nostr.nsec", TEST_KEY_2_NSEC)?; |
| 43 | config.set_str("nostr.npub", TEST_KEY_2_NPUB)?; | 44 | config.set_str("nostr.npub", TEST_KEY_2_NPUB)?; |
| 45 | config.set_str("user.name", "test name")?; | ||
| 46 | config.set_str("user.email", "test@test.com")?; | ||
| 47 | config.set_bool("commit.gpgSign", false)?; | ||
| 44 | Ok(()) | 48 | Ok(()) |
| 45 | } | 49 | } |
| 46 | 50 | ||
| @@ -808,8 +812,6 @@ mod fetch { | |||
| 808 | 812 | ||
| 809 | mod push { | 813 | mod push { |
| 810 | 814 | ||
| 811 | use nostr_sdk::Event; | ||
| 812 | |||
| 813 | use super::*; | 815 | use super::*; |
| 814 | 816 | ||
| 815 | #[tokio::test] | 817 | #[tokio::test] |
| @@ -1712,6 +1714,150 @@ mod push { | |||
| 1712 | 1714 | ||
| 1713 | #[tokio::test] | 1715 | #[tokio::test] |
| 1714 | #[serial] | 1716 | #[serial] |
| 1717 | async fn proposal_merge_commit_pushed_to_main_leads_to_status_event_issued() -> Result<()> { | ||
| 1718 | // | ||
| 1719 | let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?; | ||
| 1720 | let source_path = source_git_repo.dir.to_str().unwrap().to_string(); | ||
| 1721 | |||
| 1722 | let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = ( | ||
| 1723 | Relay::new(8051, None, None), | ||
| 1724 | Relay::new(8052, None, None), | ||
| 1725 | Relay::new(8053, None, None), | ||
| 1726 | Relay::new(8055, None, None), | ||
| 1727 | Relay::new(8056, None, None), | ||
| 1728 | Relay::new(8057, None, None), | ||
| 1729 | ); | ||
| 1730 | r51.events = events.clone(); | ||
| 1731 | r55.events = events.clone(); | ||
| 1732 | |||
| 1733 | let before = r55.events.iter().cloned().collect::<HashSet<Event>>(); | ||
| 1734 | |||
| 1735 | let cli_tester_handle = std::thread::spawn(move || -> Result<(String, Oid)> { | ||
| 1736 | let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?; | ||
| 1737 | |||
| 1738 | let git_repo = clone_git_repo_with_nostr_url()?; | ||
| 1739 | git_repo.checkout_remote_branch(&branch_name)?; | ||
| 1740 | git_repo.checkout("refs/heads/main")?; | ||
| 1741 | |||
| 1742 | std::fs::write(git_repo.dir.join("new.md"), "some content")?; | ||
| 1743 | git_repo.stage_and_commit("new.md")?; | ||
| 1744 | |||
| 1745 | CliTester::new_git_with_remote_helper_from_dir( | ||
| 1746 | &git_repo.dir, | ||
| 1747 | ["merge", &branch_name, "-m", "proposal merge commit message"], | ||
| 1748 | ) | ||
| 1749 | .expect_end_eventually_and_print()?; | ||
| 1750 | |||
| 1751 | let oid = git_repo.get_tip_of_local_branch("main")?; | ||
| 1752 | |||
| 1753 | let mut p = CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push"]); | ||
| 1754 | cli_expect_nostr_fetch(&mut p)?; | ||
| 1755 | p.expect(format!("fetching refs list: {}...\r\n\r", source_path).as_str())?; | ||
| 1756 | p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?; | ||
| 1757 | let output = p.expect_end_eventually()?; | ||
| 1758 | |||
| 1759 | for p in [51, 52, 53, 55, 56, 57] { | ||
| 1760 | relay::shutdown_relay(8000 + p)?; | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | Ok((output, oid)) | ||
| 1764 | }); | ||
| 1765 | // launch relays | ||
| 1766 | let _ = join!( | ||
| 1767 | r51.listen_until_close(), | ||
| 1768 | r52.listen_until_close(), | ||
| 1769 | r53.listen_until_close(), | ||
| 1770 | r55.listen_until_close(), | ||
| 1771 | r56.listen_until_close(), | ||
| 1772 | r57.listen_until_close(), | ||
| 1773 | ); | ||
| 1774 | |||
| 1775 | let (output, oid) = cli_tester_handle.join().unwrap()?; | ||
| 1776 | |||
| 1777 | assert_eq!( | ||
| 1778 | output, | ||
| 1779 | format!(" 431b84e..{} main -> main\r\n", &oid.to_string()[..7]) | ||
| 1780 | ); | ||
| 1781 | |||
| 1782 | let new_events = r55 | ||
| 1783 | .events | ||
| 1784 | .iter() | ||
| 1785 | .cloned() | ||
| 1786 | .collect::<HashSet<Event>>() | ||
| 1787 | .difference(&before) | ||
| 1788 | .cloned() | ||
| 1789 | .collect::<Vec<Event>>(); | ||
| 1790 | |||
| 1791 | assert_eq!(new_events.len(), 2, "{new_events:?}"); | ||
| 1792 | |||
| 1793 | let proposal = r55 | ||
| 1794 | .events | ||
| 1795 | .iter() | ||
| 1796 | .find(|e| { | ||
| 1797 | e.iter_tags() | ||
| 1798 | .find(|t| t.as_vec()[0].eq("branch-name")) | ||
| 1799 | .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1)) | ||
| 1800 | }) | ||
| 1801 | .unwrap(); | ||
| 1802 | |||
| 1803 | let merge_status = new_events | ||
| 1804 | .iter() | ||
| 1805 | .find(|e| e.kind().eq(&Kind::GitStatusApplied)) | ||
| 1806 | .unwrap(); | ||
| 1807 | |||
| 1808 | assert_eq!( | ||
| 1809 | oid.to_string(), | ||
| 1810 | merge_status | ||
| 1811 | .tags | ||
| 1812 | .iter() | ||
| 1813 | .find(|t| t.as_vec()[0].eq("merge-commit-id")) | ||
| 1814 | .unwrap() | ||
| 1815 | .as_vec()[1], | ||
| 1816 | "status sets correct merge-commit-id tag" | ||
| 1817 | ); | ||
| 1818 | |||
| 1819 | let proposal_tip = r55 | ||
| 1820 | .events | ||
| 1821 | .iter() | ||
| 1822 | .filter(|e| { | ||
| 1823 | e.iter_tags() | ||
| 1824 | .any(|t| t.as_vec()[1].eq(&proposal.id().to_string())) | ||
| 1825 | && e.kind().eq(&Kind::GitPatch) | ||
| 1826 | }) | ||
| 1827 | .last() | ||
| 1828 | .unwrap(); | ||
| 1829 | |||
| 1830 | assert_eq!( | ||
| 1831 | proposal_tip.id().to_string(), | ||
| 1832 | merge_status | ||
| 1833 | .tags | ||
| 1834 | .iter() | ||
| 1835 | .find(|t| t.as_vec().len().eq(&4) && t.as_vec()[3].eq("mention")) | ||
| 1836 | .unwrap() | ||
| 1837 | .as_vec()[1], | ||
| 1838 | "status mentions proposal tip event \r\nmerge status:\r\n{}\r\nproposal tip:\r\n{}", | ||
| 1839 | merge_status.as_json(), | ||
| 1840 | proposal_tip.as_json(), | ||
| 1841 | ); | ||
| 1842 | |||
| 1843 | assert_eq!( | ||
| 1844 | proposal.id().to_string(), | ||
| 1845 | merge_status | ||
| 1846 | .tags | ||
| 1847 | .iter() | ||
| 1848 | .find(|t| t.is_root()) | ||
| 1849 | .unwrap() | ||
| 1850 | .as_vec()[1], | ||
| 1851 | "status tags proposal id as root \r\nmerge status:\r\n{}\r\nproposal:\r\n{}", | ||
| 1852 | merge_status.as_json(), | ||
| 1853 | proposal.as_json(), | ||
| 1854 | ); | ||
| 1855 | |||
| 1856 | Ok(()) | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | #[tokio::test] | ||
| 1860 | #[serial] | ||
| 1715 | async fn push_2_commits_to_existing_proposal() -> Result<()> { | 1861 | async fn push_2_commits_to_existing_proposal() -> Result<()> { |
| 1716 | let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?; | 1862 | let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?; |
| 1717 | let source_path = source_git_repo.dir.to_str().unwrap().to_string(); | 1863 | let source_path = source_git_repo.dir.to_str().unwrap().to_string(); |