Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh

Commit

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
syntaqxcommitted Parentc678dcaBrowse files
4 files+40−70/4 viewed
+7−0
138138 /// from the one whose merge is recorded by whoever landed it.
139139 #[serde(skip_serializing_if = "Option::is_none")]
140140 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>,
141146 }
142147
143148 /// A branch or tag deleted by a push. `before` is the commit it pointed to.
14061411 caused_by_job: job_run_of(&actor).map(str::to_owned),
14071412 commits: Some(3),
14081413 landed_pull: None,
1414+ agent: None,
14091415 };
14101416 let push = serde_json::to_value(push).unwrap();
14111417 assert_eq!(caused_by_job(&push), Some("run_9"));
14251431 caused_by_job: None,
14261432 commits: None,
14271433 landed_pull: None,
1434+ agent: None,
14281435 };
14291436 let quiet = serde_json::to_value(push(false)).unwrap();
14301437 assert!(quiet.get("unscanned").is_none());
+16−1
19011901 commits: Option<u32>,
19021902 ) -> Result<()> {
19031903 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)
19051906 .await
19061907 }
19071908
19271928 caused_by_job,
19281929 commits,
19291930 a.pull,
1931+ acting_agent(&a.actor),
19301932 )
19311933 .await
19321934 }
19451947 caused_by_job: Option<String>,
19461948 commits: Option<u32>,
19471949 landed_pull: Option<u32>,
1950+ agent: Option<String>,
19481951 ) -> Result<()> {
19491952 self.publish(NewEvent {
19501953 kind: "git.push",
19621965 caused_by_job,
19631966 commits,
19641967 landed_pull,
1968+ agent,
19651969 },
19661970 })
19671971 .await
23862390 .as_ref()
23872391 .filter(|user| user.kind == PrincipalKind::User && g1t_contracts::events::job_run_of(user).is_none())
23882392 .map(|user| user.id.clone()),
2393+ agent: viewer.as_ref().and_then(acting_agent),
23892394 actor: viewer.map(|user: User| user.id),
23902395 unscanned: forwarded.unscanned,
23912396 });
25432548 unscanned,
25442549 caused_by_job,
25452550 credit,
2551+ agent,
25462552 } = push;
25472553 // What the push stored, for billing's storage meter. A failure only
25482554 // leaves the count short.
26162622 caused_by_job.clone(),
26172623 commits,
26182624 None,
2625+ agent.clone(),
26192626 )
26202627 .await?;
26212628 }
26882695 /// The person credited with the push's commits on their contribution
26892696 /// calendar: whoever pushed, when that is a person (push_commits.rs).
26902697 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())
26912705 }
26922706
26932707 /// What a git request leaves for after its answer: its audit entry, with
34313445 unscanned: false,
34323446 caused_by_job: None,
34333447 credit: None,
3448+ agent: None,
34343449 }
34353450 }
34363451
+1−1
892892 Some(session) => Some(session),
893893 None => self.caller_session(&repo.id, a.number, &a.actor).await?,
894894 };
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? {
896896 return Ok(Outcome::fail(FailureCode::Conflict, "This issue is already closed."));
897897 }
898898 return Ok(match self.issue(&repo.id, a.number).await? {
+16−5
174174 merged.push(recorded.id);
175175 }
176176
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();
177179 if closes_issues {
178180 let path = RepoPath { namespace: repo.namespace.clone(), name: repo.name.clone() };
179181 for (number, commit) in commit_closes(&pushed.commits, &path) {
180182 let message = pushed.commits.iter().find(|pushed| pushed.hash == commit).map(|pushed| pushed.message.as_str());
181183 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?;
183185 }
184186 }
185187 Ok(merged)
187189
188190 /// Closes an open issue as completed by a commit pushed to the default
189191 /// 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.
192195 pub(crate) async fn close_by_commit(
193196 &self,
194197 repo: &Repo,
196199 commit: &str,
197200 actor: &User,
198201 session: Option<&str>,
202+ agent: bool,
199203 ) -> Result<bool> {
200204 let Some(issue) = self.issue(&repo.id, number).await?.filter(|issue| issue.state == State::Open) else {
201205 return Ok(false);
228232 },
229233 )
230234 .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?;
233244 self.issue_resolved(&repo.id, number, actor, true, None).await;
234245 Ok(true)
235246 }

Comments

Sign in to comment.