flagon-io/g1t

public

Git for AI scale: a forge for thousands of agents working on the same code at once.

g1t/apps/api/src/renamed.rs

80 lines2,645 bytesCodeBlame

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.

Agents and memory, checks and conflicts, profiles, slug renames, custom domains1//! Old workspace slugs, after a rename. An operation that names a
2//! repository (`owner/name`) or a workspace by a slug the workspace has
3//! since been renamed from is run again under the slug it has now, so
4//! scripts and agents written against the old address keep working while
5//! it redirects.
6
7use g1t_contracts::identity::SlugArgs;
8use serde_json::Value;
9use worker::Result;
10
11use crate::operations::Services;
12
13/// The workspace slug an operation's input names: the owner in `repo`, or
14/// `workspace`.
15pub fn named_slug(input: &Value) -> Option<&str> {
16 input["repo"]
17 .as_str()
18 .and_then(|repo| repo.split_once('/'))
19 .map(|(owner, _)| owner)
20 .or_else(|| input["workspace"].as_str())
21 .filter(|slug| !slug.is_empty())
22}
23
24/// `input` with `old` replaced by `new` wherever it names the workspace.
25pub fn rewrite(input: &Value, old: &str, new: &str) -> Value {
26 let mut input = input.clone();
27 if let Some(repo) = input["repo"].as_str()
28 && let Some((owner, name)) = repo.split_once('/')
29 && owner.eq_ignore_ascii_case(old)
30 {
31 input["repo"] = Value::String(format!("{new}/{name}"));
32 }
33 if input["workspace"]
34 .as_str()
35 .is_some_and(|workspace| workspace.eq_ignore_ascii_case(old))
36 {
37 input["workspace"] = Value::String(new.to_owned());
38 }
39 input
40}
41
42/// The input under the workspace's current slug, if it names an old one.
43pub async fn retarget(services: &Services, input: &Value) -> Result<Option<Value>> {
44 let Some(old) = named_slug(input) else {
45 return Ok(None);
46 };
47 let current: Option<String> = g1t_kit::call(
48 &services.identity,
49 "resolve_slug",
50 &SlugArgs {
51 slug: old.to_owned(),
52 },
53 )
54 .await?;
55 Ok(current.map(|new| rewrite(input, old, &new)))
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use serde_json::json;
62
63 #[test]
64 fn finds_the_slug_an_operation_names() {
65 assert_eq!(named_slug(&json!({ "repo": "acme/rocket" })), Some("acme"));
66 assert_eq!(named_slug(&json!({ "workspace": "acme" })), Some("acme"));
67 assert_eq!(named_slug(&json!({ "id": "x" })), None);
68 }
69
70 #[test]
71 fn rewrites_only_the_old_slug() {
72 let input = json!({ "repo": "Acme/rocket", "workspace": "acme", "title": "acme/x" });
73 assert_eq!(
74 rewrite(&input, "acme", "acme-inc"),
75 json!({ "repo": "acme-inc/rocket", "workspace": "acme-inc", "title": "acme/x" })
76 );
77 let other = json!({ "repo": "globex/rocket" });
78 assert_eq!(rewrite(&other, "acme", "acme-inc"), other);
79 }
80}