Skip to content

Commit 322529d

Browse files
Justin Poehneltjpoehnelt-bot
andauthored
fix: document all environment variables and enable CONFIG_DIR override (npm#222)
* docs: document all environment variables and enable CONFIG_DIR override (npm#171) * docs: clarify env vars are trusted inputs in AGENTS.md * chore: add Gemini Code Assist style guide --------- Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
1 parent 6daf90d commit 322529d

7 files changed

Lines changed: 135 additions & 19 deletions

File tree

.changeset/document-env-vars.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@googleworkspace/cli": patch
3+
---
4+
5+
Document all environment variables and enable GOOGLE_WORKSPACE_CLI_CONFIG_DIR in release builds

.env.example

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
1-
# OAuth Client Credentials
2-
# Create these at https://console.cloud.google.com/apis/credentials
3-
GOOGLE_WORKSPACE_CLI_CLIENT_ID=
4-
GOOGLE_WORKSPACE_CLI_CLIENT_SECRET=
1+
# gws — Google Workspace CLI
2+
# Copy this file to .env and uncomment the variables you need.
3+
# All variables are optional. See README.md for details.
54

6-
# Authentication
7-
# Path to a service account JSON key file or user credentials
5+
# ── Authentication ────────────────────────────────────────────────
6+
# Pre-obtained OAuth2 access token (highest priority; bypasses all credential loading)
7+
# GOOGLE_WORKSPACE_CLI_TOKEN=
8+
9+
# Path to OAuth credentials JSON (user or service account)
810
# GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=
911

10-
# Impersonation (Domain-Wide Delegation)
11-
# Email address of the user to impersonate when using a service account
12+
# Default account email for multi-account usage (overridden by --account flag)
13+
# GOOGLE_WORKSPACE_CLI_ACCOUNT=
14+
15+
# Email of user to impersonate via Domain-Wide Delegation (service accounts only)
1216
# GOOGLE_WORKSPACE_CLI_IMPERSONATED_USER=
1317

14-
# Model Armor Sanitization
15-
# Default template resource name for --sanitize
16-
# GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE=projects/my-project/locations/us-central1/templates/my-template
17-
# Sanitization mode: 'warn' (default) or 'block'
18+
# ── OAuth Client ──────────────────────────────────────────────────
19+
# OAuth client ID and secret (alternative to saving client_secret.json)
20+
# GOOGLE_WORKSPACE_CLI_CLIENT_ID=
21+
# GOOGLE_WORKSPACE_CLI_CLIENT_SECRET=
22+
23+
# ── Configuration ─────────────────────────────────────────────────
24+
# Override the config directory (default: ~/.config/gws)
25+
# GOOGLE_WORKSPACE_CLI_CONFIG_DIR=
26+
27+
# ── Model Armor (response sanitization) ──────────────────────────
28+
# Default Model Armor template (overridden by --sanitize flag)
29+
# GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE=
30+
# Sanitization mode: warn (default) or block
1831
# GOOGLE_WORKSPACE_CLI_SANITIZE_MODE=warn
32+
33+
# ── Helpers ───────────────────────────────────────────────────────
34+
# GCP project ID fallback for gmail watch and events subscribe (overridden by --project)
35+
# GOOGLE_WORKSPACE_PROJECT_ID=

.gemini/style_guide.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Code Review Style Guide
2+
3+
## Project Architecture
4+
5+
`gws` is a Rust CLI that dynamically generates commands from Google Discovery Documents at runtime. It does NOT use generated Rust crates (`google-drive3`, etc.) for API interaction. Do not suggest adding API-specific crates to `Cargo.toml`.
6+
7+
## Security: Trusted vs Untrusted Inputs
8+
9+
This CLI is frequently invoked by AI/LLM agents. CLI arguments may be adversarial.
10+
11+
- **CLI arguments (untrusted)** — Must validate paths against traversal (`../../`), reject control characters, percent-encode URL path segments, and use `reqwest .query()` for query parameters. Validators: `validate_safe_output_dir()`, `validate_safe_dir_path()`, `encode_path_segment()`, `validate_resource_name()`.
12+
- **Environment variables (trusted)** — Set by the user in their shell profile, `.env` file, or deployment config. Do NOT flag missing path validation on environment variable values. This is consistent with `XDG_CONFIG_HOME`, `CARGO_HOME`, etc.
13+
14+
## Test Coverage
15+
16+
The `codecov/patch` check requires new/modified lines to be covered by tests. Prefer extracting testable helper functions over embedding logic in `main`/`run`. Tests should cover both happy paths and rejection paths (e.g., pass `../../.ssh` and assert `Err`).
17+
18+
## Changesets
19+
20+
Every PR must include a `.changeset/<name>.md` file. Use `patch` for fixes/chores, `minor` for features, `major` for breaking changes.
21+
22+
## Code Style
23+
24+
- Rust: `cargo clippy -- -D warnings` must pass. `cargo fmt` enforced via pre-commit hook.
25+
- Node.js: Use `pnpm` not `npm`.
26+
- OAuth scope strings in test code will trigger "restricted/sensitive scope" warnings — these are expected and should be ignored.

AGENTS.md

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ ASCII art title cards live in `art/`. The `scripts/show-art.sh` helper clears th
8484
> [!IMPORTANT]
8585
> This CLI is frequently invoked by AI/LLM agents. Always assume inputs can be adversarial — validate paths against traversal (`../../.ssh`), restrict format strings to allowlists, reject control characters, and encode user values before embedding them in URLs.
8686
87+
> [!NOTE]
88+
> **Environment variables are trusted inputs.** The validation rules above apply to **CLI arguments** that may be passed by untrusted AI agents. Environment variables (e.g. `GOOGLE_WORKSPACE_CLI_CONFIG_DIR`) are set by the user themselves — in their shell profile, `.env` file, or deployment config — and are not subject to path traversal validation. This is consistent with standard conventions like `XDG_CONFIG_HOME`, `CARGO_HOME`, etc.
89+
8790
### Path Safety (`src/validate.rs`)
8891

8992
When adding new helpers or CLI flags that accept file paths, **always validate** using the shared helpers:
@@ -165,7 +168,40 @@ Use these labels to categorize pull requests and issues:
165168

166169
## Environment Variables
167170

168-
- `GOOGLE_WORKSPACE_CLI_TOKEN` — Pre-obtained OAuth2 access token (highest priority; bypasses all credential file loading)
169-
- `GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE` — Path to OAuth credentials JSON (no default; if unset, falls back to credentials secured by the OS Keyring and encrypted in `~/.config/gws/`)
170-
- `GOOGLE_WORKSPACE_CLI_ACCOUNT` — Default account email for multi-account usage (overridden by `--account` flag)
171-
- Supports `.env` files via `dotenvy`
171+
### Authentication
172+
173+
| Variable | Description |
174+
|---|---|
175+
| `GOOGLE_WORKSPACE_CLI_TOKEN` | Pre-obtained OAuth2 access token (highest priority; bypasses all credential file loading) |
176+
| `GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE` | Path to OAuth credentials JSON (no default; if unset, falls back to credentials secured by the OS Keyring and encrypted in `~/.config/gws/`) |
177+
| `GOOGLE_WORKSPACE_CLI_ACCOUNT` | Default account email for multi-account usage (overridden by `--account` flag) |
178+
| `GOOGLE_WORKSPACE_CLI_IMPERSONATED_USER` | Email of user to impersonate with Domain-Wide Delegation (service accounts only) |
179+
| `GOOGLE_APPLICATION_CREDENTIALS` | Standard Google ADC path; used as fallback when no gws-specific credentials are configured |
180+
181+
### Configuration
182+
183+
| Variable | Description |
184+
|---|---|
185+
| `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` | Override the config directory (default: `~/.config/gws`) |
186+
187+
### OAuth Client
188+
189+
| Variable | Description |
190+
|---|---|
191+
| `GOOGLE_WORKSPACE_CLI_CLIENT_ID` | OAuth client ID (for `gws auth login` when no `client_secret.json` is saved) |
192+
| `GOOGLE_WORKSPACE_CLI_CLIENT_SECRET` | OAuth client secret (paired with `CLIENT_ID` above) |
193+
194+
### Sanitization (Model Armor)
195+
196+
| Variable | Description |
197+
|---|---|
198+
| `GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE` | Default Model Armor template (overridden by `--sanitize` flag) |
199+
| `GOOGLE_WORKSPACE_CLI_SANITIZE_MODE` | `warn` (default) or `block` |
200+
201+
### Helpers
202+
203+
| Variable | Description |
204+
|---|---|
205+
| `GOOGLE_WORKSPACE_PROJECT_ID` | GCP project ID fallback for `gmail watch` and `events subscribe` helpers (overridden by `--project` flag) |
206+
207+
All variables can also live in a `.env` file (loaded via `dotenvy`).

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ npm install -g @googleworkspace/cli
3333
- [AI Agent Skills](#ai-agent-skills)
3434
- [MCP Server](#mcp-server)
3535
- [Advanced Usage](#advanced-usage)
36+
- [Environment Variables](#environment-variables)
3637
- [Architecture](#architecture)
3738
- [Troubleshooting](#troubleshooting)
3839
- [Development](#development)
@@ -356,6 +357,25 @@ gws gmail users messages get --params '...' \
356357
| `GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE` | Default Model Armor template |
357358
| `GOOGLE_WORKSPACE_CLI_SANITIZE_MODE` | `warn` (default) or `block` |
358359

360+
## Environment Variables
361+
362+
All variables are optional. See [`.env.example`](.env.example) for a copy-paste template.
363+
364+
| Variable | Description |
365+
|---|---|
366+
| `GOOGLE_WORKSPACE_CLI_TOKEN` | Pre-obtained OAuth2 access token (highest priority) |
367+
| `GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE` | Path to OAuth credentials JSON (user or service account) |
368+
| `GOOGLE_WORKSPACE_CLI_ACCOUNT` | Default account email (overridden by `--account` flag) |
369+
| `GOOGLE_WORKSPACE_CLI_IMPERSONATED_USER` | Email for Domain-Wide Delegation (service accounts) |
370+
| `GOOGLE_WORKSPACE_CLI_CLIENT_ID` | OAuth client ID (alternative to `client_secret.json`) |
371+
| `GOOGLE_WORKSPACE_CLI_CLIENT_SECRET` | OAuth client secret (paired with `CLIENT_ID`) |
372+
| `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` | Override config directory (default: `~/.config/gws`) |
373+
| `GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE` | Default Model Armor template |
374+
| `GOOGLE_WORKSPACE_CLI_SANITIZE_MODE` | `warn` (default) or `block` |
375+
| `GOOGLE_WORKSPACE_PROJECT_ID` | GCP project ID fallback for helper commands |
376+
377+
Environment variables can also be set in a `.env` file (loaded via [dotenvy](https://crates.io/crates/dotenvy)).
378+
359379
## Architecture
360380

361381
`gws` uses a **two-phase parsing** strategy:

src/auth_commands.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ const READONLY_SCOPES: &[&str] = &[
9292
];
9393

9494
pub fn config_dir() -> PathBuf {
95-
#[cfg(test)]
9695
if let Ok(dir) = std::env::var("GOOGLE_WORKSPACE_CLI_CONFIG_DIR") {
9796
return PathBuf::from(dir);
9897
}
@@ -266,7 +265,7 @@ async fn handle_login(args: &[String]) -> Result<(), GwsError> {
266265
}
267266

268267
// Determine scopes: explicit flags > interactive TUI > defaults
269-
let mut scopes = resolve_scopes(
268+
let scopes = resolve_scopes(
270269
&filtered_args,
271270
project_id.as_deref(),
272271
services_filter.as_ref(),
@@ -277,7 +276,7 @@ async fn handle_login(args: &[String]) -> Result<(), GwsError> {
277276
// gmail.metadata blocks query parameters like `q`, and is redundant
278277
// when broader scopes (gmail.modify, gmail.readonly, mail.google.com)
279278
// are already included.
280-
let scopes = filter_redundant_restrictive_scopes(scopes);
279+
let mut scopes = filter_redundant_restrictive_scopes(scopes);
281280

282281
let secret = yup_oauth2::ApplicationSecret {
283282
client_id: client_id.clone(),

src/main.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,19 @@ fn print_usage() {
454454
println!(
455455
" GOOGLE_WORKSPACE_CLI_ACCOUNT Default account email for multi-account"
456456
);
457+
println!(
458+
" GOOGLE_WORKSPACE_CLI_IMPERSONATED_USER Email for Domain-Wide Delegation (service accounts)"
459+
);
460+
println!(
461+
" GOOGLE_WORKSPACE_CLI_CONFIG_DIR Override config directory (default: ~/.config/gws)"
462+
);
463+
println!(" GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE Default Model Armor template");
464+
println!(
465+
" GOOGLE_WORKSPACE_CLI_SANITIZE_MODE Sanitization mode: warn (default) or block"
466+
);
467+
println!(
468+
" GOOGLE_WORKSPACE_PROJECT_ID GCP project ID fallback for helper commands"
469+
);
457470
println!();
458471
println!("COMMUNITY:");
459472
println!(" Star the repo: https://github.com/googleworkspace/cli");

0 commit comments

Comments
 (0)