fix: use gcloud.cmd on Windows and show platform-correct config paths (#126)

* fix: use gcloud.cmd on Windows and show platform-correct config paths

On Windows, Google Cloud SDK installs `gcloud.cmd` (not `gcloud.exe`).
Rust's `Command::new("gcloud")` does not search PATHEXT, so all gcloud
invocations failed silently. Add `gcloud_bin()` helper that returns
`gcloud.cmd` on Windows and `gcloud` elsewhere.

Also replace hardcoded `~/.config/gws/` paths in error messages with
the actual platform-resolved path (`%APPDATA%\gws\` on Windows).

* chore: add changeset for Windows gcloud compat fix
This commit is contained in:
Frank
2026-03-05 14:48:27 +08:00
committed by GitHub
parent 75cec1b444
commit 263a8e5479
3 changed files with 45 additions and 12 deletions
+9
View File
@@ -0,0 +1,9 @@
---
"@googleworkspace/cli": patch
---
fix: use gcloud.cmd on Windows and show platform-correct config paths
On Windows, gcloud is installed as `gcloud.cmd` which Rust's `Command`
cannot find without the extension. Also replaced hardcoded `~/.config/gws/`
in error messages with the actual platform-resolved path.
+9 -7
View File
@@ -463,13 +463,15 @@ fn resolve_client_credentials() -> Result<(String, String, Option<String>), GwsE
Some(config.project_id),
)),
Err(_) => Err(GwsError::Auth(
"No OAuth client configured.\n\n\
Either:\n \
1. Run `gws auth setup` to configure a GCP project and OAuth client\n \
2. Download client_secret.json from Google Cloud Console and save it to:\n \
~/.config/gws/client_secret.json\n \
3. Set env vars: GOOGLE_WORKSPACE_CLI_CLIENT_ID and GOOGLE_WORKSPACE_CLI_CLIENT_SECRET"
.to_string(),
format!(
"No OAuth client configured.\n\n\
Either:\n \
1. Run `gws auth setup` to configure a GCP project and OAuth client\n \
2. Download client_secret.json from Google Cloud Console and save it to:\n \
{}\n \
3. Set env vars: GOOGLE_WORKSPACE_CLI_CLIENT_ID and GOOGLE_WORKSPACE_CLI_CLIENT_SECRET",
crate::oauth_config::client_config_path().display()
),
)),
}
}
+27 -5
View File
@@ -399,17 +399,28 @@ pub fn parse_setup_args(args: &[String]) -> SetupOptions {
// ── gcloud helpers ──────────────────────────────────────────────
/// Returns the gcloud executable name for the current platform.
/// On Windows, gcloud is installed as `gcloud.cmd` which Rust's
/// `Command` cannot find without the extension.
fn gcloud_bin() -> &'static str {
if cfg!(windows) {
"gcloud.cmd"
} else {
"gcloud"
}
}
/// Create a gcloud Command with interactive prompts disabled.
/// This prevents CBA proxy install prompts from blocking subprocess calls.
fn gcloud_cmd() -> Command {
let mut cmd = Command::new("gcloud");
let mut cmd = Command::new(gcloud_bin());
cmd.env("CLOUDSDK_CORE_DISABLE_PROMPTS", "1");
cmd
}
/// Check if gcloud CLI is installed.
pub fn is_gcloud_installed() -> bool {
Command::new("gcloud")
Command::new(gcloud_bin())
.arg("version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
@@ -650,7 +661,7 @@ async fn enable_apis(
.map(|api_id| {
let project_id = project_id.to_string();
async move {
let result = tokio::process::Command::new("gcloud")
let result = tokio::process::Command::new(gcloud_bin())
.env("CLOUDSDK_CORE_DISABLE_PROMPTS", "1")
.args(["services", "enable", &api_id, "--project", &project_id])
.stdout(std::process::Stdio::null())
@@ -1262,7 +1273,7 @@ fn manual_oauth_instructions(project_id: &str) -> String {
" gws auth login\n\n",
" Option B — Download the JSON file:\n",
" Download 'client_secret_*.json' from the Cloud Console dialog\n",
" and save it to: ~/.config/gws/client_secret.json\n",
" and save it to: {config_path}\n",
" Then run: gws auth login\n\n",
" Option C — Re-run setup interactively (recommended for first-time setup):\n",
" gws auth setup\n\n",
@@ -1270,7 +1281,8 @@ fn manual_oauth_instructions(project_id: &str) -> String {
"Desktop app clients do not require you to register a redirect URI manually."
),
consent_url = consent_url,
creds_url = creds_url
creds_url = creds_url,
config_path = crate::oauth_config::client_config_path().display()
)
}
@@ -1965,4 +1977,14 @@ mod tests {
.collect();
assert!(json_failed.is_empty());
}
#[test]
fn gcloud_bin_returns_platform_appropriate_name() {
let bin = gcloud_bin();
if cfg!(windows) {
assert_eq!(bin, "gcloud.cmd");
} else {
assert_eq!(bin, "gcloud");
}
}
}