Skip to content

g1t/apps/api/src/renamed.rs

135 lines4,640 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.

Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1//! Old addresses: a workspace's slug after a rename, and a repository's
2//! path after a transfer.
3//!
Agents and memory, checks and conflicts, profiles, slug renames, custom domains4//! 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.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains11
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
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look63/// 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
Agents and memory, checks and conflicts, profiles, slug renames, custom domains90#[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]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look103 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]
Agents and memory, checks and conflicts, profiles, slug renames, custom domains126 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.