Skip to content

Commit 44ec3a3

Browse files
runningcodeclaude
andcommitted
feat(build): preserve repository name case for build upload
Add case-preserving VCS functions for build upload command only. Other commands continue using lowercase names. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4c70555 commit 44ec3a3

2 files changed

Lines changed: 87 additions & 5 deletions

File tree

src/commands/build/upload.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use crate::utils::fs::TempDir;
2424
use crate::utils::fs::TempFile;
2525
use crate::utils::progress::ProgressBar;
2626
use crate::utils::vcs::{
27-
self, get_github_pr_number, get_provider_from_remote, get_repo_from_remote, git_repo_base_ref,
28-
git_repo_base_repo_name, git_repo_head_ref, git_repo_remote_url,
27+
self, get_github_pr_number, get_provider_from_remote, get_repo_from_remote_preserve_case,
28+
git_repo_base_ref, git_repo_base_repo_name_preserve_case, git_repo_head_ref,
29+
git_repo_remote_url,
2930
};
3031

3132
pub fn make_command(command: Command) -> Command {
@@ -140,7 +141,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
140141
.or_else(|| {
141142
remote_url
142143
.as_ref()
143-
.map(|url| get_repo_from_remote(url))
144+
.map(|url| get_repo_from_remote_preserve_case(url))
144145
.map(Cow::Owned)
145146
});
146147

@@ -198,7 +199,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
198199
.or_else(|| {
199200
// Try to get the base repo name from the VCS if not provided
200201
repo_ref
201-
.and_then(|r| match git_repo_base_repo_name(r) {
202+
.and_then(|r| match git_repo_base_repo_name_preserve_case(r) {
202203
Ok(Some(base_repo_name)) => {
203204
debug!("Found base repository name: {}", base_repo_name);
204205
Some(base_repo_name)

src/utils/vcs.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,32 @@ pub fn get_repo_from_remote(repo: &str) -> String {
221221
obj.id
222222
}
223223

224+
/// Like get_repo_from_remote but preserves the original case of the repository name.
225+
/// This is used specifically for build upload where case preservation is important.
226+
pub fn get_repo_from_remote_preserve_case(repo: &str) -> String {
227+
// First get the lowercase version to ensure we handle all the complex VCS URL patterns correctly
228+
let lowercase_result = get_repo_from_remote(repo);
229+
230+
// Then try to extract the case-preserved repository path using simple regex patterns
231+
lazy_static! {
232+
// Matches most common Git URL patterns to extract the repository path
233+
static ref REPO_PATH_RE: Regex = Regex::new(
234+
r"(?:https?://[^/]+/|git@[^:/]+[:/]|ssh://(?:[^@]+@)?[^/]+/)(.+?)(?:\.git)?/?$"
235+
).unwrap();
236+
}
237+
238+
if let Some(caps) = REPO_PATH_RE.captures(repo) {
239+
let case_preserved_path = &caps[1];
240+
// Use the case-preserved version if it looks like a valid repo path (contains '/')
241+
if case_preserved_path.contains('/') && case_preserved_path.len() > 1 {
242+
return case_preserved_path.to_string();
243+
}
244+
}
245+
246+
// Fall back to the lowercase version if regex extraction fails
247+
lowercase_result
248+
}
249+
224250
pub fn get_provider_from_remote(remote: &str) -> String {
225251
let obj = VcsUrl::parse(remote);
226252
extract_provider_name(&obj.provider).to_owned()
@@ -292,6 +318,19 @@ fn find_merge_base_ref(
292318
/// Prefers "upstream" remote if it exists, then "origin", otherwise uses the first available remote.
293319
/// Returns the base repository name if a remote is found.
294320
pub fn git_repo_base_repo_name(repo: &git2::Repository) -> Result<Option<String>> {
321+
git_repo_base_repo_name_impl(repo, false)
322+
}
323+
324+
/// Like git_repo_base_repo_name but preserves the original case of the repository name.
325+
/// This is used specifically for build upload where case preservation is important.
326+
pub fn git_repo_base_repo_name_preserve_case(repo: &git2::Repository) -> Result<Option<String>> {
327+
git_repo_base_repo_name_impl(repo, true)
328+
}
329+
330+
fn git_repo_base_repo_name_impl(
331+
repo: &git2::Repository,
332+
preserve_case: bool,
333+
) -> Result<Option<String>> {
295334
let remotes = repo.remotes()?;
296335
let remote_names: Vec<&str> = remotes.iter().flatten().collect();
297336

@@ -312,7 +351,12 @@ pub fn git_repo_base_repo_name(repo: &git2::Repository) -> Result<Option<String>
312351
match git_repo_remote_url(repo, chosen_remote) {
313352
Ok(remote_url) => {
314353
debug!("Found remote '{}': {}", chosen_remote, remote_url);
315-
Ok(Some(get_repo_from_remote(&remote_url)))
354+
let repo_name = if preserve_case {
355+
get_repo_from_remote_preserve_case(&remote_url)
356+
} else {
357+
get_repo_from_remote(&remote_url)
358+
};
359+
Ok(Some(repo_name))
316360
}
317361
Err(e) => {
318362
warn!("Could not get URL for remote '{}': {}", chosen_remote, e);
@@ -927,6 +971,43 @@ mod tests {
927971
);
928972
}
929973

974+
#[test]
975+
fn test_get_repo_from_remote_preserve_case() {
976+
// Test that case-preserving function maintains original casing
977+
assert_eq!(
978+
get_repo_from_remote_preserve_case("https://github.com/MyOrg/MyRepo"),
979+
"MyOrg/MyRepo"
980+
);
981+
assert_eq!(
982+
get_repo_from_remote_preserve_case("git@github.com:SentryOrg/SentryRepo.git"),
983+
"SentryOrg/SentryRepo"
984+
);
985+
assert_eq!(
986+
get_repo_from_remote_preserve_case("https://gitlab.com/MyCompany/MyProject"),
987+
"MyCompany/MyProject"
988+
);
989+
assert_eq!(
990+
get_repo_from_remote_preserve_case("git@bitbucket.org:TeamName/ProjectName.git"),
991+
"TeamName/ProjectName"
992+
);
993+
assert_eq!(
994+
get_repo_from_remote_preserve_case("ssh://git@github.com/MyUser/MyRepo.git"),
995+
"MyUser/MyRepo"
996+
);
997+
998+
// Test that regular function still lowercases
999+
assert_eq!(
1000+
get_repo_from_remote("https://github.com/MyOrg/MyRepo"),
1001+
"myorg/myrepo"
1002+
);
1003+
1004+
// Test edge cases - should fall back to lowercase when regex doesn't match
1005+
assert_eq!(
1006+
get_repo_from_remote_preserve_case("invalid-url"),
1007+
get_repo_from_remote("invalid-url")
1008+
);
1009+
}
1010+
9301011
#[test]
9311012
fn test_extract_provider_name() {
9321013
// Test basic provider name extraction

0 commit comments

Comments
 (0)