diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1e54b..ab18129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.0] - 2026-01-08 +### Changed +- **Major Redesign**: `gsw` now defaults to **Session-scoped switching** (current terminal tab only) for improved safety. +- `gsw `: Switches configuration for the current session by default. +- `gsw -g ` or `gsw --global `: Switches the global configuration. +- Simplified help and usage messages to reflect the new session-first approach. + +### Removed +- `gsw-local` command has been completely removed (its functionality is now the default behavior of `gsw`). + ## [0.2.0] - 2026-01-07 ### Added - `--help` / `-h` flag for both `gsw` and `gsw-local` commands. diff --git a/LLMS.txt b/LLMS.txt index 38657be..27e6538 100644 --- a/LLMS.txt +++ b/LLMS.txt @@ -13,30 +13,21 @@ gsw is a lightweight Bash/Zsh plugin for switching between gcloud configurations ### gsw -Switch the GLOBAL active gcloud configuration. +Switch the gcloud configuration. Defaults to the current shell session. ``` -gsw # Switch to specified configuration +gsw # Switch for current session (sets CLOUDSDK_ACTIVE_CONFIG_NAME) +gsw -g # Switch GLOBALLY (changes default active config) gsw # List available configurations and show usage gsw --help # Show help (static, no gcloud call) gsw --version # Show version ``` -### gsw-local - -Switch configuration ONLY for the current shell session. - -``` -gsw-local # Switch locally (sets CLOUDSDK_ACTIVE_CONFIG_NAME) -gsw-local # List available configurations and show usage -gsw-local --help # Show help -``` - ## Environment Variables | Variable | Description | Set by | |----------|-------------|--------| -| `CLOUDSDK_ACTIVE_CONFIG_NAME` | Overrides global config for current shell | `gsw-local` | +| `CLOUDSDK_ACTIVE_CONFIG_NAME` | Overrides global config for current shell | `gsw` (default) | ## Exit Codes diff --git a/README.md b/README.md index 7981719..13b7755 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,22 @@ > Switch Google Cloud configurations instantly. -`gsw` is a lightweight Bash/Zsh plugin that makes switching between Google Cloud SDK configurations effortless. It supports both **Global** switching (changing the default for all shells) and **Local** switching (changing only for the current shell session). +`gsw` is a lightweight Bash/Zsh plugin that makes switching between Google Cloud SDK configurations effortless. It defaults to **Session** switching (changing only for the current terminal tab), with an option for **Global** switching. ## Why gsw? Managing multiple Google Cloud projects usually involves `gcloud config configurations activate ...`, which changes the global state. This becomes dangerous when you have multiple terminals openβ€”running a command in one terminal might target the wrong project because you switched configs in another tab. `gsw` solves this by offering: -1. **Safety**: `gsw-local` limits configuration changes to only the *current shell session* (using `CLOUDSDK_ACTIVE_CONFIG_NAME`). -2. **Speed**: Shorter aliases (`gsw`, `gsw-local`) with tab completion save you keystrokes. -3. **Simplicity**: No complex setup or dependencies. Just shell functions. +1. **Safety by Default**: `gsw ` limits configuration changes to only the *current shell session* (using `CLOUDSDK_ACTIVE_CONFIG_NAME`). +2. **Global Control**: Use `gsw -g ` when you actually want to change the default for all terminals. +3. **Speed**: Shorter command with tab completion saves you keystrokes. +4. **Simplicity**: No complex setup or dependencies. Just shell functions. ## Features -- ⚑ **Fast Switching**: `gsw ` to switch globally. -- πŸ›‘οΈ **Local Isolation**: `gsw-local ` to switch *only* in the current terminal tab (perfect for multi-project workflows). +- πŸ›‘οΈ **Session Isolation**: `gsw ` switches *only* in the current terminal tab (perfect for multi-project workflows). +- ⚑ **Global Switch**: `gsw -g ` to switch globally when needed. - 🧠 **Auto-Completion**: Tab completion for your existing gcloud configurations (Bash & Zsh). - πŸ“¦ **Zero Dependencies**: Pure Shell functions. @@ -69,18 +70,18 @@ use = ["gsw.sh"] ## Usage -### Global Switch (`gsw`) -Changes the active configuration for **all** open terminals (that rely on the global config). +### Session Switch (Default) +Changes the active configuration **only for the current shell session**. This sets the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable. ```bash gsw my-work-profile ``` -### Local Switch (`gsw-local`) -Changes the active configuration **only for the current shell session**. This sets the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable. +### Global Switch (`-g`) +Changes the active configuration for **all** open terminals. ```bash -gsw-local my-personal-profile +gsw -g my-personal-profile ``` ### List Configurations diff --git a/demo.gif b/demo.gif index 238e3c6..3ad615c 100644 Binary files a/demo.gif and b/demo.gif differ diff --git a/demo.tape b/demo.tape index 93f7515..65a1892 100644 --- a/demo.tape +++ b/demo.tape @@ -25,16 +25,14 @@ Type "setopt PROMPT_SUBST" Enter Type "setopt INTERACTIVE_COMMENTS" Enter -# Mock gcloud for simple list usage if needed (though gsw mock handles most) +# Mock gcloud for simple list usage Type "function gcloud() { :; }" Enter - -# OVERRIDE gsw/gsw-local -# Using single quotes for VHS 'Type' makes it much safer to define functions with double quotes internally -Type 'function gsw() { if [ -z "$1" ]; then echo "Available gcloud configurations:"; echo "NAME IS_ACTIVE ACCOUNT PROJECT"; echo "default True user@demo.com demo-project"; echo "work-project False worker@corp.com work-production"; echo "personal-side-hustle False me@gmail.com personal-app"; echo ""; echo "Usage: gsw "; echo " Switches the GLOBAL active configuration."; else echo "Activated [$1]."; echo "βœ… Switched to configuration: $1"; echo "Current Identity:"; echo "- user@demo.com"; echo "- $1-id"; export PS1="%F{blue}%1~%f %F{cyan}(gcloud:$1)%f %# "; fi }' +Type 'GSW_GLOBAL="default"; GSW_SESSION="";' Enter -Type 'function gsw-local() { echo "βœ… Switched LOCALLY to configuration: $1"; echo "(This change only affects this terminal tab)"; echo "Current Identity:"; echo "- user@demo.com"; echo "- $1-id"; export CLOUDSDK_ACTIVE_CONFIG_NAME="$1"; export PS1="%F{blue}%1~%f %F{cyan}(gcloud:$1)%f %# "; }' +# OVERRIDE gsw for demo (Mocks output and PS1 updates with state) +Type 'function gsw() { local is_global=false; local config=""; while [[ $# -gt 0 ]]; do case "$1" in -g|--global) is_global=true; shift ;; *) config="$1"; shift ;; esac; done; if [ -z "$config" ]; then echo "Available gcloud configurations:"; echo "NAME IS_ACTIVE ACCOUNT PROJECT"; echo "default True user@demo.com demo-project"; echo "work-project False worker@corp.com work-production"; echo "personal-side-hustle False me@gmail.com personal-app"; echo ""; echo "Usage: gsw [options] "; return; fi; if [ "$is_global" = true ]; then GSW_GLOBAL="$config"; echo "βœ… Switched GLOBALLY to configuration: $config"; else GSW_SESSION="$config"; export CLOUDSDK_ACTIVE_CONFIG_NAME="$config"; echo "βœ… Switched SESSION to configuration: $config"; echo "(This change only affects this terminal tab)"; fi; local active=${GSW_SESSION:-$GSW_GLOBAL}; export PS1="%F{blue}%1~%f %F{cyan}(gcloud:$active)%f %# "; }' Enter # 2. Configure PS1 (Initial State) @@ -46,23 +44,21 @@ Enter Show # Start the demo -# (Removed no-arg 'gsw' check as requested) - -Type "# Switch global configuration easily" +Type "# First, switch the Global configuration" Enter Sleep 500ms -Type "gsw work-project" +Type "gsw -g work-project" Enter Sleep 2s -Type "# Or switch LOCALLY for just this shell session" +Type "# Now, switch SESSION for just this terminal tab" Enter Sleep 500ms -Type "gsw-local personal-side-hustle" +Type "gsw personal-side-hustle" Enter Sleep 2s -Type "# Verify: Only this shell session is affected (env var is set)" +Type "# Verify: Environment variable is set, overriding global" Enter Sleep 500ms Type "echo $CLOUDSDK_ACTIVE_CONFIG_NAME" diff --git a/gsw.sh b/gsw.sh index d0617a0..0b8e6ce 100644 --- a/gsw.sh +++ b/gsw.sh @@ -2,95 +2,75 @@ # gsw: Google Switch # Easily switch between gcloud configurations -GSW_VERSION="v0.2.0" +GSW_VERSION="v0.3.0" function gsw() { - if [ "$1" = "--version" ] || [ "$1" = "-v" ]; then - echo "gsw version $GSW_VERSION" - return 0 - fi + local is_global=false + local config_name="" - if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then - echo "Usage: gsw " - echo " gsw --help" - echo " gsw --version" - echo "" - echo " Switches the GLOBAL active gcloud configuration." - echo "" - echo "Options:" - echo " Name of the gcloud configuration to activate" - echo " --help, -h Show this help message" - echo " --version, -v Show version information" - echo "" - echo "πŸ’‘ Tip: Run 'gsw' without arguments to see available configurations." - return 0 - fi + while [[ $# -gt 0 ]]; do + case "$1" in + --version|-v) + echo "gsw version $GSW_VERSION" + return 0 + ;; + --help|-h) + echo "Usage: gsw [options] " + echo "" + echo " Switches the gcloud configuration for the current session by default." + echo "" + echo "Options:" + echo " Name of the gcloud configuration to activate" + echo " -g, --global Switch the GLOBAL active configuration" + echo " -h, --help Show this help message" + echo " -v, --version Show version information" + echo "" + echo "πŸ’‘ Tip: Run 'gsw' without arguments to see available configurations." + return 0 + ;; + -g|--global) + is_global=true + shift + ;; + -*) + echo "Error: Unknown option $1" + return 1 + ;; + *) + config_name="$1" + shift + ;; + esac + done - if [ -z "$1" ]; then + if [ -z "$config_name" ]; then echo "Available gcloud configurations:" gcloud config configurations list echo "" - echo "Usage: gsw " - echo " gsw --help" - echo " gsw --version" - echo " Switches the GLOBAL active configuration." + echo "Usage: gsw [options] " + echo " (Default switches only for this terminal session)" echo "" - echo "πŸ’‘ Tip: To switch automatically when entering a directory," - echo " add this to your .envrc file:" - echo " export CLOUDSDK_ACTIVE_CONFIG_NAME=\"\"" + echo "Options:" + echo " -g, --global Switch the GLOBAL active configuration" return fi # Check if the configuration exists - if ! gcloud config configurations describe "$1" > /dev/null 2>&1; then - echo "Error: Configuration '$1' does not exist." + if ! gcloud config configurations describe "$config_name" > /dev/null 2>&1; then + echo "Error: Configuration '$config_name' does not exist." echo "Available configurations:" gcloud config configurations list return 1 fi - - gcloud config configurations activate "$1" - echo "βœ… Switched to configuration: $1" - - # Optional: Show current active account and project for confirmation - echo "Current Identity:" - gcloud config list --format="value(core.account,core.project)" | tr '\t' '\n' | sed 's/^/- /' -} -# gsw-local: Switch ONLY in the current shell -# Uses environment variable CLOUDSDK_ACTIVE_CONFIG_NAME -function gsw-local() { - if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then - echo "Usage: gsw-local " - echo " gsw-local --help" - echo "" - echo " Switches configuration ONLY for this terminal session." - echo " This sets the CLOUDSDK_ACTIVE_CONFIG_NAME environment variable." - echo "" - echo "Options:" - echo " Name of the gcloud configuration to activate locally" - echo " --help, -h Show this help message" - echo "" - echo "πŸ’‘ Tip: Run 'gsw-local' without arguments to see available configurations." - return 0 + if [ "$is_global" = true ]; then + gcloud config configurations activate "$config_name" + echo "βœ… Switched GLOBALLY to configuration: $config_name" + else + export CLOUDSDK_ACTIVE_CONFIG_NAME="$config_name" + echo "βœ… Switched SESSION to configuration: $config_name" + echo "(This change only affects this terminal tab)" fi - - if [ -z "$1" ]; then - echo "Available gcloud configurations:" - gcloud config configurations list - echo "" - echo "Usage: gsw-local " - echo " gsw-local --help" - echo " Switches configuration ONLY for this terminal tab." - echo "" - echo "πŸ’‘ Tip: This sets the environment variable:" - echo " export CLOUDSDK_ACTIVE_CONFIG_NAME=\"\"" - return - fi - - export CLOUDSDK_ACTIVE_CONFIG_NAME="$1" - echo "βœ… Switched LOCALLY to configuration: $1" - echo "(This change only affects this terminal tab)" echo "Current Identity:" gcloud config list --format="value(core.account,core.project)" | tr '\t' '\n' | sed 's/^/- /' @@ -111,7 +91,6 @@ if [ -n "$BASH_VERSION" ]; then return 0 } complete -F _gsw_bash_autocomplete gsw - complete -F _gsw_bash_autocomplete gsw-local elif [ -n "$ZSH_VERSION" ]; then # Zsh Completion @@ -121,5 +100,4 @@ elif [ -n "$ZSH_VERSION" ]; then compadd -a configs } compdef _gsw gsw - compdef _gsw gsw-local fi diff --git a/test/gsw.bats b/test/gsw.bats index e584ad7..5e650a0 100644 --- a/test/gsw.bats +++ b/test/gsw.bats @@ -22,60 +22,60 @@ setup() { source ./gsw.sh || true # Ignore errors specifically for the non-bash parts for now } -@test "gsw-local sets CLOUDSDK_ACTIVE_CONFIG_NAME" { - gsw-local "test-config" +@test "gsw sets CLOUDSDK_ACTIVE_CONFIG_NAME by default" { + gsw "test-config" [ "$CLOUDSDK_ACTIVE_CONFIG_NAME" = "test-config" ] } -@test "gsw activation calls gcloud config configurations activate" { - # We need to mock the 'describe' call to succeed, otherwise gsw returns early. - # Our mock just echoes, which counts as success (exit 0) and output. - - run gsw "test-config" +@test "gsw -g calls gcloud config configurations activate" { + run gsw -g "test-config" [ "$status" -eq 0 ] - # Check if it tried to activate + [[ "$output" =~ "Switched GLOBALLY" ]] [[ "$output" =~ "gcloud config configurations activate test-config" ]] } -@test "gsw with no args shows usage" { +@test "gsw --global calls gcloud config configurations activate" { + run gsw --global "test-config" + [ "$status" -eq 0 ] + [[ "$output" =~ "Switched GLOBALLY" ]] +} + +@test "gsw with no args shows usage with session info" { run gsw [ "$status" -eq 0 ] - [[ "$output" =~ "Usage: gsw" ]] + [[ "$output" =~ "Default switches only for this terminal session" ]] } @test "gsw --version outputs version" { run gsw --version [ "$status" -eq 0 ] - # Check format: gsw version vX.X.X [[ "$output" =~ "gsw version v"[0-9]+\.[0-9]+\.[0-9]+ ]] } -@test "gsw --help shows usage without config list" { +@test "gsw --help shows usage with flags" { run gsw --help [ "$status" -eq 0 ] - [[ "$output" =~ "Usage: gsw" ]] - [[ "$output" =~ "--help" ]] - # Should NOT call gcloud (no config list) + [[ "$output" =~ "-g, --global" ]] [[ ! "$output" =~ "gcloud config configurations list" ]] } -@test "gsw -h shows usage" { - run gsw -h - [ "$status" -eq 0 ] - [[ "$output" =~ "Usage: gsw" ]] +@test "gsw returns error for unknown option" { + run gsw --unknown + [ "$status" -eq 1 ] + [[ "$output" =~ "Error: Unknown option" ]] } -@test "gsw-local --help shows usage without config list" { - run gsw-local --help - [ "$status" -eq 0 ] - [[ "$output" =~ "Usage: gsw-local" ]] - [[ "$output" =~ "--help" ]] - # Should NOT call gcloud (no config list) - [[ ! "$output" =~ "gcloud config configurations list" ]] -} - -@test "gsw-local -h shows usage" { - run gsw-local -h - [ "$status" -eq 0 ] - [[ "$output" =~ "Usage: gsw-local" ]] +@test "gsw returns error for non-existent config" { + # Mock failure for 'describe' + function gcloud() { + if [[ "$*" =~ "describe" ]]; then + return 1 + fi + echo "gcloud $*" + } + export -f gcloud + + run gsw "invalid-config" + [ "$status" -eq 1 ] + [[ "$output" =~ "Error: Configuration 'invalid-config' does not exist" ]] }