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
135 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Money in billing's messages reads to the cent, and the deploy reads migrations again after a failure1//! Old addresses: a workspace's slug after a rename, and a repository's
2//! path after a transfer.
3//!
4//! Old workspace slugs, after a rename. An operation that names a
5//! repository (`owner/name`) or a workspace by a slug the workspace has
6//! since been renamed from is run again under the slug it has now, so
7//! scripts and agents written against the old address keep working while
Merge branch 'worktree-agent-a8385d293d42c913a'8//! it redirects. A workspace alias g1t's staff set (identity's aliases.rs:
9//! `g1t` for `flagon-io`) resolves the same way, for good, so responses
10//! name the workspace by its own slug.
Money in billing's messages reads to the cent, and the deploy reads migrations again after a failure11
12use g1t_contracts::identity::SlugArgs;
13use serde_json::Value;
14use worker::Result;
15
16use crate::operations::Services;
17
18/// The workspace slug an operation's input names: the owner in `repo`, or
19/// `workspace`.
20pub fn named_slug(input: &Value) -> Option<&str> {
21 input["repo"]
22 .as_str()
23 .and_then(|repo| repo.split_once('/'))
24 .map(|(owner, _)| owner)
25 .or_else(|| input["workspace"].as_str())
26 .filter(|slug| !slug.is_empty())
27}
28
29/// `input` with `old` replaced by `new` wherever it names the workspace.
30pub fn rewrite(input: &Value, old: &str, new: &str) -> Value {
31 let mut input = input.clone();
32 if let Some(repo) = input["repo"].as_str()
33 && let Some((owner, name)) = repo.split_once('/')
34 && owner.eq_ignore_ascii_case(old)
35 {
36 input["repo"] = Value::String(format!("{new}/{name}"));
37 }
38 if input["workspace"]
39 .as_str()
40 .is_some_and(|workspace| workspace.eq_ignore_ascii_case(old))
41 {
42 input["workspace"] = Value::String(new.to_owned());
43 }
44 input
45}
46
47/// The input under the workspace's current slug, if it names an old one.
48pub async fn retarget(services: &Services, input: &Value) -> Result<Option<Value>> {
49 let Some(old) = named_slug(input) else {
50 return Ok(None);
51 };
52 let current: Option<String> = g1t_kit::call(
53 &services.identity,
54 "resolve_slug",
55 &SlugArgs {
56 slug: old.to_owned(),
57 },
58 )
59 .await?;
60 Ok(current.map(|new| rewrite(input, old, &new)))
61}
62
63/// The input under a transferred repository's path now, if `repo` names
64/// the path it left.
65pub async fn transferred(services: &Services, input: &Value) -> Result<Option<Value>> {
66 let Some((namespace, name)) = input["repo"].as_str().and_then(|repo| repo.split_once('/')) else {
67 return Ok(None);
68 };
69 let now: Option<g1t_contracts::repos::RepoPath> = g1t_kit::call(
70 &services.repos,
71 "resolve_path",
72 &g1t_contracts::repos::ResolvePathArgs {
73 path: g1t_contracts::repos::RepoPath {
74 namespace: namespace.to_owned(),
75 name: name.to_owned(),
76 },
77 },
78 )
79 .await?;
80 Ok(now.map(|path| moved_to(input, &format!("{}/{}", path.namespace, path.name))))
81}
82
83/// `input` with `repo` replaced by `path`.
84pub fn moved_to(input: &Value, path: &str) -> Value {
85 let mut input = input.clone();
86 input["repo"] = Value::String(path.to_owned());
87 input
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use serde_json::json;
94
95 #[test]
96 fn finds_the_slug_an_operation_names() {
97 assert_eq!(named_slug(&json!({ "repo": "acme/rocket" })), Some("acme"));
98 assert_eq!(named_slug(&json!({ "workspace": "acme" })), Some("acme"));
99 assert_eq!(named_slug(&json!({ "id": "x" })), None);
100 }
101
102 #[test]
103 fn a_transferred_repository_is_named_by_its_new_path() {
104 let input = json!({ "repo": "syntaqx/g1t", "number": 4 });
105 assert_eq!(
106 moved_to(&input, "flagon-io/g1t"),
107 json!({ "repo": "flagon-io/g1t", "number": 4 })
108 );
109 }
110
111 #[test]
Merge branch 'worktree-agent-a8385d293d42c913a'112 fn an_alias_is_run_again_under_its_workspace() {
113 let input = json!({ "repo": "g1t/g1t", "number": 7 });
114 assert_eq!(named_slug(&input), Some("g1t"));
115 assert_eq!(
116 rewrite(&input, "g1t", "flagon-io"),
117 json!({ "repo": "flagon-io/g1t", "number": 7 })
118 );
119 assert_eq!(
120 rewrite(&json!({ "workspace": "G1T" }), "g1t", "flagon-io"),
121 json!({ "workspace": "flagon-io" })
122 );
123 }
124
125 #[test]
Money in billing's messages reads to the cent, and the deploy reads migrations again after a failure126 fn rewrites_only_the_old_slug() {
127 let input = json!({ "repo": "Acme/rocket", "workspace": "acme", "title": "acme/x" });
128 assert_eq!(
129 rewrite(&input, "acme", "acme-inc"),
130 json!({ "repo": "acme-inc/rocket", "workspace": "acme-inc", "title": "acme/x" })
131 );
132 let other = json!({ "repo": "globex/rocket" });
133 assert_eq!(rewrite(&other, "acme", "acme-inc"), other);
134 }
135}

This file's history is long; its oldest lines are credited to the oldest commit read.