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
26 changes: 26 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Enforce LF line endings across the team, regardless of each dev's local git config.
# Text files are auto-detected and normalized to LF in the repo and on checkout.
* text=auto eol=lf

# Common binary types — never apply line-ending conversion.
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.pdf binary
*.zip binary
*.gz binary
*.tar binary
*.7z binary
*.mp3 binary
*.wav binary
*.ogg binary
*.mp4 binary
*.mov binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.eot binary
2 changes: 1 addition & 1 deletion src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ pub async fn login_with_browser() -> Result<LoginResult> {
let stdin_is_terminal = io::stdin().is_terminal();
print_opening_browser();
print_auth_url(&manual_auth_url);
let _ = open::that(&auto_auth_url);
let _ = crate::browser::open_url(&auto_auth_url);
let mut auth_code_prompt_started = false;

loop {
Expand Down
54 changes: 54 additions & 0 deletions src/browser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Open URLs in the user's default browser, with a WSL-aware fallback.
//!
//! Under WSL the [`open`] crate tries `wslview` (from the `wslu` package) and
//! then `xdg-open`/`gio open`. When `wslu` isn't installed those Linux openers
//! report success but never reach the Windows host browser, so nothing happens.
//! On WSL we therefore hand the URL to Windows ourselves.

use std::process::Command;

/// Open `url` in the user's default browser.
pub fn open_url(url: &str) -> std::io::Result<()> {
if is_wsl() {
return open_on_windows(url);
}
open::that(url)
}

/// Detect a WSL environment by inspecting the kernel release string, which
/// contains `microsoft` (WSL2) or `Microsoft` (WSL1).
fn is_wsl() -> bool {
std::fs::read_to_string("/proc/sys/kernel/osrelease")
.map(|s| {
let s = s.to_ascii_lowercase();
s.contains("microsoft") || s.contains("wsl")
})
.unwrap_or(false)
}

/// Try each Windows-side opener in turn, returning as soon as one succeeds.
fn open_on_windows(url: &str) -> std::io::Result<()> {
// `wslview` (wslu) respects the user's configured default browser when present.
if run_ok(Command::new("wslview").arg(url)) {
return Ok(());
}
// PowerShell ships with every WSL host. Single-quote the URL so `&` in query
// strings is treated literally, doubling any embedded single quotes.
let script = format!("Start-Process '{}'", url.replace('\'', "''"));
if run_ok(Command::new("powershell.exe").args([
"-NoProfile",
"-NonInteractive",
"-Command",
&script,
])) {
return Ok(());
}
Err(std::io::Error::other(
"no working browser opener found (tried wslview and powershell.exe)",
))
}

/// Run `cmd` to completion, reporting whether it exited successfully.
fn run_ok(cmd: &mut Command) -> bool {
cmd.status().map(|s| s.success()).unwrap_or(false)
}
5 changes: 3 additions & 2 deletions src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ pub async fn handle_dev(config_path: Option<PathBuf>, verbose: bool, no_open: bo
}
if no_open {
println!(" --no-open set; open {} in your browser to start.", local_url);
} else {
open::that(&local_url)?;
} else if let Err(e) = crate::browser::open_url(&local_url) {
println!(" Couldn't open a browser automatically ({e}).");
println!(" Open {} in your browser to start.", local_url);
}

server::run(
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod achievements;
mod auth;
mod browser;
mod builds;
mod config;
mod dev;
Expand Down
Loading