What a session's push closes is g1t's, for the person: on flagon-io/session-lab #1 the session g1t started pushed its first version with Closes #1, and the issue closed as it should, but its timeline said "syntaqx closed this in 20d5574 via session …", because the git.push event carries only the pusher's id (the person, for a session's token), so work wrote the close as theirs. repos now says on git.push which agent pushed for the person (GitPush::agent, from the viewer's acting, on HTTPS pushes, landings and pushes made on g1t), and work writes the close note as g1t with acting_for the pusher when it is set; a close through the API keeps following its caller (rows::By). Deploy: repos and work together. Checked: cargo test -p g1t-contracts (316) -p g1t-repos (364) -p g1t-work (203); wasm32 check of g1t-repos and g1t-work.
- G1t-Session:
- ses_01m4z09g22e7ysqpg2ha423cws
4 files+40−70/4 viewed
| 138 | 138 | /// from the one whose merge is recorded by whoever landed it. | |
| 139 | 139 | #[serde(skip_serializing_if = "Option::is_none")] | |
| 140 | 140 | pub landed_pull: Option<u32>, | |
| 141 | + | /// Set when an agent pushed on the person's behalf (a session g1t | |
| 142 | + | /// started for them): the agent's name. The event's actor is still the | |
| 143 | + | /// person; what the push closes is written as the agent's, for them. | |
| 144 | + | #[serde(skip_serializing_if = "Option::is_none")] | |
| 145 | + | pub agent: Option<String>, | |
| 141 | 146 | } | |
| 142 | 147 | ||
| 143 | 148 | /// A branch or tag deleted by a push. `before` is the commit it pointed to. | |
| ⋯ | |||
| 1406 | 1411 | caused_by_job: job_run_of(&actor).map(str::to_owned), | |
| 1407 | 1412 | commits: Some(3), | |
| 1408 | 1413 | landed_pull: None, | |
| 1414 | + | agent: None, | |
| 1409 | 1415 | }; | |
| 1410 | 1416 | let push = serde_json::to_value(push).unwrap(); | |
| 1411 | 1417 | assert_eq!(caused_by_job(&push), Some("run_9")); | |
| ⋯ | |||
| 1425 | 1431 | caused_by_job: None, | |
| 1426 | 1432 | commits: None, | |
| 1427 | 1433 | landed_pull: None, | |
| 1434 | + | agent: None, | |
| 1428 | 1435 | }; | |
| 1429 | 1436 | let quiet = serde_json::to_value(push(false)).unwrap(); | |
| 1430 | 1437 | assert!(quiet.get("unscanned").is_none()); | |
| 1901 | 1901 | commits: Option<u32>, | |
| 1902 | 1902 | ) -> Result<()> { | |
| 1903 | 1903 | let caused_by_job = actor.and_then(g1t_contracts::events::job_run_of).map(str::to_owned); | |
| 1904 | − | self.publish_git_push(repo, git_ref, before, after, actor.map(|user| user.id.clone()), false, caused_by_job, commits, None) | |
| 1904 | + | let agent = actor.and_then(acting_agent); | |
| 1905 | + | self.publish_git_push(repo, git_ref, before, after, actor.map(|user| user.id.clone()), false, caused_by_job, commits, None, agent) | |
| 1905 | 1906 | .await | |
| 1906 | 1907 | } | |
| 1907 | 1908 | ||
| ⋯ | |||
| 1927 | 1928 | caused_by_job, | |
| 1928 | 1929 | commits, | |
| 1929 | 1930 | a.pull, | |
| 1931 | + | acting_agent(&a.actor), | |
| 1930 | 1932 | ) | |
| 1931 | 1933 | .await | |
| 1932 | 1934 | } | |
| ⋯ | |||
| 1945 | 1947 | caused_by_job: Option<String>, | |
| 1946 | 1948 | commits: Option<u32>, | |
| 1947 | 1949 | landed_pull: Option<u32>, | |
| 1950 | + | agent: Option<String>, | |
| 1948 | 1951 | ) -> Result<()> { | |
| 1949 | 1952 | self.publish(NewEvent { | |
| 1950 | 1953 | kind: "git.push", | |
| ⋯ | |||
| 1962 | 1965 | caused_by_job, | |
| 1963 | 1966 | commits, | |
| 1964 | 1967 | landed_pull, | |
| 1968 | + | agent, | |
| 1965 | 1969 | }, | |
| 1966 | 1970 | }) | |
| 1967 | 1971 | .await | |
| ⋯ | |||
| 2386 | 2390 | .as_ref() | |
| 2387 | 2391 | .filter(|user| user.kind == PrincipalKind::User && g1t_contracts::events::job_run_of(user).is_none()) | |
| 2388 | 2392 | .map(|user| user.id.clone()), | |
| 2393 | + | agent: viewer.as_ref().and_then(acting_agent), | |
| 2389 | 2394 | actor: viewer.map(|user: User| user.id), | |
| 2390 | 2395 | unscanned: forwarded.unscanned, | |
| 2391 | 2396 | }); | |
| ⋯ | |||
| 2543 | 2548 | unscanned, | |
| 2544 | 2549 | caused_by_job, | |
| 2545 | 2550 | credit, | |
| 2551 | + | agent, | |
| 2546 | 2552 | } = push; | |
| 2547 | 2553 | // What the push stored, for billing's storage meter. A failure only | |
| 2548 | 2554 | // leaves the count short. | |
| ⋯ | |||
| 2616 | 2622 | caused_by_job.clone(), | |
| 2617 | 2623 | commits, | |
| 2618 | 2624 | None, | |
| 2625 | + | agent.clone(), | |
| 2619 | 2626 | ) | |
| 2620 | 2627 | .await?; | |
| 2621 | 2628 | } | |
| ⋯ | |||
| 2688 | 2695 | /// The person credited with the push's commits on their contribution | |
| 2689 | 2696 | /// calendar: whoever pushed, when that is a person (push_commits.rs). | |
| 2690 | 2697 | credit: Option<String>, | |
| 2698 | + | /// The agent that pushed for the person, when one did (GitPush::agent). | |
| 2699 | + | agent: Option<String>, | |
| 2700 | + | } | |
| 2701 | + | ||
| 2702 | + | /// The agent a viewer acts as for a person: g1t, for a session it started. | |
| 2703 | + | fn acting_agent(user: &User) -> Option<String> { | |
| 2704 | + | user.acting.as_deref().map(|acting| acting.agent.clone()) | |
| 2691 | 2705 | } | |
| 2692 | 2706 | ||
| 2693 | 2707 | /// What a git request leaves for after its answer: its audit entry, with | |
| ⋯ | |||
| 3431 | 3445 | unscanned: false, | |
| 3432 | 3446 | caused_by_job: None, | |
| 3433 | 3447 | credit: None, | |
| 3448 | + | agent: None, | |
| 3434 | 3449 | } | |
| 3435 | 3450 | } | |
| 3436 | 3451 | ||
| 892 | 892 | Some(session) => Some(session), | |
| 893 | 893 | None => self.caller_session(&repo.id, a.number, &a.actor).await?, | |
| 894 | 894 | }; | |
| 895 | − | if !self.close_by_commit(&repo, a.number, &commit, &a.actor, session.as_deref()).await? { | |
| 895 | + | if !self.close_by_commit(&repo, a.number, &commit, &a.actor, session.as_deref(), false).await? { | |
| 896 | 896 | return Ok(Outcome::fail(FailureCode::Conflict, "This issue is already closed.")); | |
| 897 | 897 | } | |
| 898 | 898 | return Ok(match self.issue(&repo.id, a.number).await? { |
| 174 | 174 | merged.push(recorded.id); | |
| 175 | 175 | } | |
| 176 | 176 | ||
| 177 | + | // A session g1t started pushed it: what it closes is g1t's, for the pusher. | |
| 178 | + | let agent = event.data["agent"].as_str().is_some(); | |
| 177 | 179 | if closes_issues { | |
| 178 | 180 | let path = RepoPath { namespace: repo.namespace.clone(), name: repo.name.clone() }; | |
| 179 | 181 | for (number, commit) in commit_closes(&pushed.commits, &path) { | |
| 180 | 182 | let message = pushed.commits.iter().find(|pushed| pushed.hash == commit).map(|pushed| pushed.message.as_str()); | |
| 181 | 183 | let session = self.trailer_session(&repo.id, message.unwrap_or_default()).await?; | |
| 182 | − | self.close_by_commit(&repo, number, &commit, &actor, session.as_deref()).await?; | |
| 184 | + | self.close_by_commit(&repo, number, &commit, &actor, session.as_deref(), agent).await?; | |
| 183 | 185 | } | |
| 184 | 186 | } | |
| 185 | 187 | Ok(merged) | |
| ⋯ | |||
| 187 | 189 | ||
| 188 | 190 | /// Closes an open issue as completed by a commit pushed to the default | |
| 189 | 191 | /// branch: says so on the issue, with the session it was closed in when | |
| 190 | − | /// one is known, and lets go of what waited on it. Whether it closed it: | |
| 191 | − | /// not when it was closed already. | |
| 192 | + | /// one is known, and lets go of what waited on it. `agent` when an agent | |
| 193 | + | /// pushed it for `actor` (GitPush::agent): the note is then g1t's, for | |
| 194 | + | /// them. Whether it closed it: not when it was closed already. | |
| 192 | 195 | pub(crate) async fn close_by_commit( | |
| 193 | 196 | &self, | |
| 194 | 197 | repo: &Repo, | |
| ⋯ | |||
| 196 | 199 | commit: &str, | |
| 197 | 200 | actor: &User, | |
| 198 | 201 | session: Option<&str>, | |
| 202 | + | agent: bool, | |
| 199 | 203 | ) -> Result<bool> { | |
| 200 | 204 | let Some(issue) = self.issue(&repo.id, number).await?.filter(|issue| issue.state == State::Open) else { | |
| 201 | 205 | return Ok(false); | |
| ⋯ | |||
| 228 | 232 | }, | |
| 229 | 233 | ) | |
| 230 | 234 | .await?; | |
| 231 | − | self.note(&repo.id, number, (&actor.id, &actor.username), &closed_note(&format!("closed this in {commit}"), session)) | |
| 232 | − | .await?; | |
| 235 | + | let by = match crate::rows::By::from(actor) { | |
| 236 | + | by if agent && by.acting_for.is_none() && by.id == actor.id => crate::rows::By { | |
| 237 | + | id: g1t_contracts::identity::AGENT_ID, | |
| 238 | + | name: g1t_contracts::identity::AGENT_NAME, | |
| 239 | + | acting_for: Some((&actor.id, &actor.username)), | |
| 240 | + | }, | |
| 241 | + | by => by, | |
| 242 | + | }; | |
| 243 | + | self.note(&repo.id, number, by, &closed_note(&format!("closed this in {commit}"), session)).await?; | |
| 233 | 244 | self.issue_resolved(&repo.id, number, actor, true, None).await; | |
| 234 | 245 | Ok(true) | |
| 235 | 246 | } | |
Comments
Sign in to comment.