Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions src/git/remote_ref/azure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,22 +122,30 @@ struct AzForkRepository {
/// Prefers the primary remote (typically `origin` or whatever the user pushed
/// with) so fork workflows hit the right tenant. Falls back to the first
/// Azure remote found if the primary isn't Azure DevOps.
///
/// The host is a web host, the same shape [`parse_web_url`] returns: an
/// `ssh.dev.azure.com` remote reports `dev.azure.com`, since the URL builders
/// that consume it put the host into an HTTPS URL.
fn detect_azure_target(repo: &Repository) -> Option<(String, String)> {
let target = |parsed: &GitRemoteUrl| {
let org = parsed.azure_organization()?;
let host = if host_is_within(parsed.host(), "visualstudio.com") {
parsed.host()
} else {
"dev.azure.com"
};
Some((host.to_string(), org.to_string()))
};
if let Ok(remote) = repo.primary_remote()
&& let Some(url) = repo.effective_remote_url(&remote)
&& let Some(parsed) = GitRemoteUrl::parse(&url)
&& let Some(org) = parsed.azure_organization()
&& let Some(found) = target(&parsed)
{
return Some((parsed.host().to_string(), org.to_string()));
}
for (_, url) in repo.all_remote_urls() {
if let Some(parsed) = GitRemoteUrl::parse(&url)
&& let Some(org) = parsed.azure_organization()
{
return Some((parsed.host().to_string(), org.to_string()));
}
return Some(found);
}
None
repo.all_remote_urls()
.into_iter()
.find_map(|(_, url)| target(&GitRemoteUrl::parse(&url)?))
}

/// Build the `--org` URL for the `az` CLI from a host and organization.
Expand Down
35 changes: 35 additions & 0 deletions tests/integration_tests/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6787,6 +6787,41 @@ fn test_switch_pr_azure_fork(#[from(repo_with_remote)] repo: TestRepo) {
});
}

/// With no `webUrl` in the response, the org and host come from the local
/// remote. An `ssh.dev.azure.com` remote must still suggest an HTTPS
/// `dev.azure.com` URL for the PR's repository, not one on the SSH host.
#[rstest]
fn test_switch_pr_azure_ssh_remote_suggests_web_host(#[from(repo_with_remote)] repo: TestRepo) {
repo.run_git(&[
"remote",
"set-url",
"origin",
"git@ssh.dev.azure.com:v3/myorg/myproject/test-repo",
]);

let az_response = r#"{
"title": "Fix in a sibling repository",
"createdBy": {"uniqueName": "alice@example.com"},
"status": "active",
"isDraft": false,
"sourceRefName": "refs/heads/feature-auth",
"repository": {
"name": "other-repo",
"project": {"name": "myproject"}
},
"forkSource": null
}"#;

let mock_bin = setup_mock_az(&repo, az_response);

let settings = setup_snapshot_settings(&repo);
settings.bind(|| {
let mut cmd = make_snapshot_cmd(&repo, "switch", &["pr:101"], None);
configure_mock_cli_env(&mut cmd, &mock_bin);
assert_cmd_snapshot!("switch_pr_azure_ssh_remote_suggests_web_host", cmd);
});
}

/// A missing PR reaches the user as the `TF401174` line `az` printed.
///
/// Once the extension question is settled, `azure::fetch_pr_info` classifies
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
source: tests/integration_tests/switch.rs
info:
program: wt
args:
- switch
- "pr:101"
env:
APPDATA: "[TEST_CONFIG_HOME]"
CLAUDE_CONFIG_DIR: "[TEST_CLAUDE_CONFIG]"
CLICOLOR_FORCE: "1"
COLUMNS: "500"
GIT_ALLOW_PROTOCOL: file
GIT_AUTHOR_DATE: "2025-01-01T00:00:00Z"
GIT_AUTHOR_EMAIL: test@example.com
GIT_AUTHOR_NAME: Test User
GIT_COMMITTER_DATE: "2025-01-01T00:00:00Z"
GIT_COMMITTER_EMAIL: test@example.com
GIT_COMMITTER_NAME: Test User
GIT_CONFIG_COUNT: "2"
GIT_CONFIG_GLOBAL: /nonexistent/wt/gitconfig
GIT_CONFIG_KEY_0: user.useConfigOnly
GIT_CONFIG_KEY_1: rerere.enabled
GIT_CONFIG_SYSTEM: /nonexistent/wt/gitconfig
GIT_CONFIG_VALUE_0: "true"
GIT_CONFIG_VALUE_1: "false"
GIT_TERMINAL_PROMPT: "0"
HOME: "[TEST_HOME]"
LANG: C
LC_ALL: C
LLVM_PROFILE_FILE: "[LLVM_PROFILE_FILE]"
OPENCODE_CONFIG_DIR: "[TEST_OPENCODE_CONFIG]"
PATH: "[PATH]"
TERM: alacritty
USERPROFILE: "[TEST_HOME]"
WORKTRUNK_APPROVALS_PATH: "[TEST_APPROVALS]"
WORKTRUNK_CONFIG_PATH: "[TEST_CONFIG]"
WORKTRUNK_SYSTEM_CONFIG_PATH: "[TEST_SYSTEM_CONFIG]"
WORKTRUNK_TEST_BASH_INSTALLED: "0"
WORKTRUNK_TEST_CLAUDE_INSTALLED: "0"
WORKTRUNK_TEST_CODEX_INSTALLED: "0"
WORKTRUNK_TEST_DELAYED_STREAM_MS: "-1"
WORKTRUNK_TEST_EPOCH: "1735776000"
WORKTRUNK_TEST_FISH_INSTALLED: "0"
WORKTRUNK_TEST_GEMINI_INSTALLED: "0"
WORKTRUNK_TEST_MOCK_CONFIG_DIR: "[TEST_MOCK_CONFIG]"
WORKTRUNK_TEST_NUSHELL_ENV: "0"
WORKTRUNK_TEST_OPENCODE_INSTALLED: "0"
WORKTRUNK_TEST_PARENT_SHELL: ""
WORKTRUNK_TEST_POWERSHELL_ENV: "0"
WORKTRUNK_TEST_POWERSHELL_INSTALLED: "0"
WORKTRUNK_TEST_PROBE_TIMEOUT_MS: "60000"
WORKTRUNK_TEST_SKIP_URL_HEALTH_CHECK: "1"
WORKTRUNK_TEST_ZSH_INSTALLED: "0"
XDG_CONFIG_HOME: "[TEST_CONFIG_HOME]"
---
success: false
exit_code: 1
----- stdout -----

----- stderr -----
◎ Fetching PR #101...
  Fix in a sibling repository (#101)
  by @alice@example.com · active · feature-auth · https://dev.azure.com/myorg/myproject/_git/other-repo/pullrequest/101
✗ No remote found for myorg/other-repo
↳ Add the remote: git remote add upstream https://dev.azure.com/myorg/myproject/_git/other-repo
Loading