From 709a56a5337f26aed628561a9ed6c4c0ac5d68f8 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Fri, 18 Jul 2025 21:12:00 +0100 Subject: [PATCH 01/12] WIP: Added Lando support. 'lando panssh' works, but 'lando pssh' doesn't. --- lando/.lando.local.yml | 23 ++++++++++++++ panssh | 71 ++++++++++++++++++++++++------------------ 2 files changed, 64 insertions(+), 30 deletions(-) create mode 100644 lando/.lando.local.yml diff --git a/lando/.lando.local.yml b/lando/.lando.local.yml new file mode 100644 index 0000000..6489584 --- /dev/null +++ b/lando/.lando.local.yml @@ -0,0 +1,23 @@ +services: + appserver: + build_as_root: + - echo "----------------- Installing panssh ---------------" + # PanSSH main script. + - curl -so /usr/local/bin/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh + - chmod +x /usr/local/bin/panssh + # PanSSH readx utility script. + - mkdir -p /usr/local/lib/panssh/ + - curl -so /usr/local/lib/panssh/readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/readx.source.sh + # Standard Bash tab-completion support. + - apt-get -qqq update && apt-get -qqq install bash-completion + # Bash-completion support for the panssh command (and other commands in lando ssh). + # We would have to either uncomment /etc/bash.bashrc "enable bash completion" section, + # or add the equivalent into ~/.bashrc, or other suitable location. + #- mkdir -p /usr/local/share/bash-completion/completions + #- curl -so /usr/share/bash-completion/completions/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/bash-completion/panssh + build: + - hash terminus && terminus auth:login && terminus site:list --format=csv --fields=name,id > $HOME/.panssh.sites + +tooling: + panssh: + service: appserver diff --git a/panssh b/panssh index b3d849c..9b35755 100755 --- a/panssh +++ b/panssh @@ -39,38 +39,49 @@ readonly SCP_OPTIONS="-q -P $SSH_PORT $SSH_GENERAL_OPTIONS" # Location of this script. readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# --- Load SITE_IDs from config --- -if [[ ! -f "$SITES_FILE" ]]; then - echo "🟔 Sites file not found at: $SITES_FILE" - echo - echo "To create it, run:" - echo " terminus site:list --format=csv --fields=name,id > $SITES_FILE" - exit 1 +# --- Fetch details of the requested site and environment --- +if [[ -n "$PANTHEON_SITE_NAME" && -n "$PANTHEON_SITE" && -n "$PANTHEON_ENV_ID" ]]; then + # Lando, or some other situation where site details have been provided. + readonly SITE_NAME="$PANTHEON_SITE_NAME" + readonly ENV_ID="$PANTHEON_ENV_ID" + readonly SITE_ID="$PANTHEON_SITE" fi -# --- Parse command-line, get site.env argument --- -if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then - echo -e "\n$PANSSH_VERSION" - echo -e "\nUsage examples:\n" - echo -e " panssh site.env" - echo -e " panssh site.env \"commands\"" - echo -e " panssh site.env < script.sh" - echo -e " echo \"commands\" | panssh site.env" - echo -e "\nSpecial commands:\n" - echo -e " View a file: .vw " - echo -e " Edit a file: .ed " - echo -e " Toggle automatic directory listing: .ls\n" - exit 1 -fi - -# Set site and environment names, lookup site ID. -readonly SITE_NAME="${BASH_REMATCH[1]}" -readonly ENV_ID="${BASH_REMATCH[2]}" -readonly SITE_ID=$(grep -E "^$SITE_NAME," "$SITES_FILE" | cut -d',' -f2) - -if [[ -z "$SITE_ID" ]]; then - echo "āŒ Site '$SITE_NAME' not found in $SITES_FILE" >&2 - exit 1 +# If we don't already have site details, process the command line. +if [[ -z "$SITE_NAME" ]]; then + # Parse the command-line: get site.env argument or display usage info. + if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then + echo -e "\n$PANSSH_VERSION" + echo -e "\nUsage examples:\n" + echo -e " panssh site.env" + echo -e " panssh site.env \"commands\"" + echo -e " panssh site.env < script.sh" + echo -e " echo \"commands\" | panssh site.env" + echo -e "\nSpecial commands:\n" + echo -e " View a file: .vw " + echo -e " Edit a file: .ed " + echo -e " Toggle automatic directory listing: .ls\n" + exit 1 + fi + + # Load info about our available sites. + if [[ ! -f "$SITES_FILE" ]]; then + echo "🟔 Sites file not found at: $SITES_FILE" + echo + echo "To create it, run:" + echo " terminus site:list --format=csv --fields=name,id > $SITES_FILE" + exit 1 + fi + + # Set site and environment names, lookup site ID. + readonly SITE_NAME="${BASH_REMATCH[1]}" + readonly ENV_ID="${BASH_REMATCH[2]}" + readonly SITE_ID=$(grep -E "^$SITE_NAME," "$SITES_FILE" | cut -d',' -f2) + + if [[ -z "$SITE_ID" ]]; then + echo "āŒ Site '$SITE_NAME' not found in $SITES_FILE" >&2 + exit 1 + fi fi readonly USER="$ENV_ID.$SITE_ID" From 609296318c73e4f36acdfddaad59ca90c0895d20 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Mon, 21 Jul 2025 12:58:49 +0100 Subject: [PATCH 02/12] Lando support. 'lando panssh' and 'lando pssh' commands are both working. --- lando/.lando.local.yml | 23 ------------- lando/.lando.panssh.yml | 49 ++++++++++++++++++++++++++++ panssh | 72 ++++++++++++++++++----------------------- 3 files changed, 80 insertions(+), 64 deletions(-) delete mode 100644 lando/.lando.local.yml create mode 100644 lando/.lando.panssh.yml diff --git a/lando/.lando.local.yml b/lando/.lando.local.yml deleted file mode 100644 index 6489584..0000000 --- a/lando/.lando.local.yml +++ /dev/null @@ -1,23 +0,0 @@ -services: - appserver: - build_as_root: - - echo "----------------- Installing panssh ---------------" - # PanSSH main script. - - curl -so /usr/local/bin/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh - - chmod +x /usr/local/bin/panssh - # PanSSH readx utility script. - - mkdir -p /usr/local/lib/panssh/ - - curl -so /usr/local/lib/panssh/readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/readx.source.sh - # Standard Bash tab-completion support. - - apt-get -qqq update && apt-get -qqq install bash-completion - # Bash-completion support for the panssh command (and other commands in lando ssh). - # We would have to either uncomment /etc/bash.bashrc "enable bash completion" section, - # or add the equivalent into ~/.bashrc, or other suitable location. - #- mkdir -p /usr/local/share/bash-completion/completions - #- curl -so /usr/share/bash-completion/completions/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/bash-completion/panssh - build: - - hash terminus && terminus auth:login && terminus site:list --format=csv --fields=name,id > $HOME/.panssh.sites - -tooling: - panssh: - service: appserver diff --git a/lando/.lando.panssh.yml b/lando/.lando.panssh.yml new file mode 100644 index 0000000..13b519e --- /dev/null +++ b/lando/.lando.panssh.yml @@ -0,0 +1,49 @@ +# This file is an optional part of the PanSSH utility. +# It provides service and tooling setup to enable use of PanSSH under Lando. +# See: https://github.com/LastCallMedia/panssh + +# Installation: +# Place this file in the same location as your application's .lando.yml file. +# Do one of: +# Rename it to lando.local.yml, or merge it into your existing .lando.local.yml file. +# Add .lando.panssh.yml into the postLandoFiles section of your ~/.lando/config.yml +# See: https://docs.lando.dev/landofile/#configuration +# +# Run lando rebuild to apply the changes. + +# Commands provided: +# +# lando panssh : starts PanSSH for the specified site and environment. +# lando pssh : starts PanSSH for the related site and environment matching your current git branch. + +services: + appserver: + build_as_root: + # - echo "Installing panssh..." + # PanSSH main script. + - curl -so /usr/local/bin/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh + - chmod +x /usr/local/bin/panssh + # PanSSH readx utility script. + - mkdir -p /usr/local/lib/panssh/ + - curl -so /usr/local/lib/panssh/readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/readx.source.sh + # Install Bash tab-completion support. + - apt-get -qqq update && apt-get -qqq install bash-completion + # Bash-completion support for the panssh command (and other commands available via `lando ssh`). + # For this to work, we would have to either uncomment /etc/bash.bashrc "enable bash completion" section, + # or include the equivalent in ~/.bashrc or some other suitable location. + # - mkdir -p /usr/local/share/bash-completion/completions + # - curl -so /usr/share/bash-completion/completions/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/bash-completion/panssh + build: + # Run terminus (if available) to create the .panssh.sites file. + - hash terminus && terminus auth:login && terminus site:list --format=csv --fields=name,id > $HOME/.panssh.sites + +tooling: + panssh: + service: appserver + description: "Opens an emulated interactive SSH connection to a Pantheon site/environment." + usage: $0 panssh . + cmd: panssh + pssh: + service: appserver + description: "Opens an emulated interactive SSH connection to the related site and environment matching your git branch." + cmd: br="$(git branch --show-current)"; test "$br" = "master" && br="dev"; panssh "$PANTHEON_SITE_NAME.$br" diff --git a/panssh b/panssh index 9b35755..8365b21 100755 --- a/panssh +++ b/panssh @@ -28,6 +28,7 @@ readonly REMOTE_ENV="HOME=\"$REMOTE_HOME\" XDG_CACHE_HOME=\"$REMOTE_HOME/.cache\ readonly SSH_SOCKET="$TEMP_DIR/ssh.socket" readonly SSH_GENERAL_OPTIONS=" \ + -o LogLevel=info \ -o ControlMaster=auto \ -o ControlPath=$SSH_SOCKET \ -o ControlPersist=5m \ @@ -39,49 +40,38 @@ readonly SCP_OPTIONS="-q -P $SSH_PORT $SSH_GENERAL_OPTIONS" # Location of this script. readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# --- Fetch details of the requested site and environment --- -if [[ -n "$PANTHEON_SITE_NAME" && -n "$PANTHEON_SITE" && -n "$PANTHEON_ENV_ID" ]]; then - # Lando, or some other situation where site details have been provided. - readonly SITE_NAME="$PANTHEON_SITE_NAME" - readonly ENV_ID="$PANTHEON_ENV_ID" - readonly SITE_ID="$PANTHEON_SITE" +# --- Load SITE_IDs from config --- +if [[ ! -f "$SITES_FILE" ]]; then + echo "🟔 Sites file not found at: $SITES_FILE" + echo + echo "To create it, run:" + echo " terminus site:list --format=csv --fields=name,id > $SITES_FILE" + exit 1 fi -# If we don't already have site details, process the command line. -if [[ -z "$SITE_NAME" ]]; then - # Parse the command-line: get site.env argument or display usage info. - if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then - echo -e "\n$PANSSH_VERSION" - echo -e "\nUsage examples:\n" - echo -e " panssh site.env" - echo -e " panssh site.env \"commands\"" - echo -e " panssh site.env < script.sh" - echo -e " echo \"commands\" | panssh site.env" - echo -e "\nSpecial commands:\n" - echo -e " View a file: .vw " - echo -e " Edit a file: .ed " - echo -e " Toggle automatic directory listing: .ls\n" - exit 1 - fi - - # Load info about our available sites. - if [[ ! -f "$SITES_FILE" ]]; then - echo "🟔 Sites file not found at: $SITES_FILE" - echo - echo "To create it, run:" - echo " terminus site:list --format=csv --fields=name,id > $SITES_FILE" - exit 1 - fi - - # Set site and environment names, lookup site ID. - readonly SITE_NAME="${BASH_REMATCH[1]}" - readonly ENV_ID="${BASH_REMATCH[2]}" - readonly SITE_ID=$(grep -E "^$SITE_NAME," "$SITES_FILE" | cut -d',' -f2) - - if [[ -z "$SITE_ID" ]]; then - echo "āŒ Site '$SITE_NAME' not found in $SITES_FILE" >&2 - exit 1 - fi +# --- Parse command-line, get site.env argument --- +if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then + echo -e "\n$PANSSH_VERSION" + echo -e "\nUsage examples:\n" + echo -e " panssh site.env" + echo -e " panssh site.env \"commands\"" + echo -e " panssh site.env < script.sh" + echo -e " echo \"commands\" | panssh site.env" + echo -e "\nSpecial commands:\n" + echo -e " View a file: .vw " + echo -e " Edit a file: .ed " + echo -e " Toggle automatic directory listing: .ls\n" + exit 1 +fi + +# Set site and environment names, lookup site ID. +readonly SITE_NAME="${BASH_REMATCH[1]}" +readonly ENV_ID="${BASH_REMATCH[2]}" +readonly SITE_ID=$(grep -E "^$SITE_NAME," "$SITES_FILE" | cut -d',' -f2) + +if [[ -z "$SITE_ID" ]]; then + echo "āŒ Site '$SITE_NAME' not found in $SITES_FILE" >&2 + exit 1 fi readonly USER="$ENV_ID.$SITE_ID" From fd0186a3fdcf2f4e32dbb00f721a4edca7bbcf18 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Mon, 21 Jul 2025 15:13:48 +0100 Subject: [PATCH 03/12] Added Lando REAMDE file. Updated main README. Bumped release number to 1.2.1. --- README.md | 25 +++++++++++++++++++------ lando/.lando.panssh.yml | 13 +++---------- lando/README.md | 30 ++++++++++++++++++++++++++++++ panssh | 5 ++++- 4 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 lando/README.md diff --git a/README.md b/README.md index 21b9561..6ff725b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# šŸ–„ļø panssh – Pantheon Interactive SSH Session Emulator +# šŸ–„ļø PanSSH – Pantheon Interactive SSH Session Emulator -`panssh` emulates an interactive SSH connection to a Pantheon site's application environment using only their available (limited) SSH service. It provides command history, local editing of remote files and an emulated current working directory. +PanSSH emulates an interactive SSH connection to a Pantheon site's application environment using only their available (limited) SSH service. It provides command history, local editing of remote files and an emulated current working directory. You can do almost everything that you could if a standard SSH login were available, and it looks and feels near identical. ### Recent changes -* Tab-completion is now included, on supporting systems: +* 1.2.1: Added `.lando.panssh.yml`, which provides easy setup of PanSSH in a [Lando](https://lando.dev/) project. +* 1.2.0: Tab-completion is now included, on supporting systems: * Local site and environment names. * Remote directory and file names. @@ -64,8 +65,20 @@ echo "commands" | panssh site.env ### No installation -* Mark the main `panssh` script as executable: `chmod +x panssh` -* Run it as just `./panssh` to see further information. +Either clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or download the individual files: + +The main script: +``` +curl -so panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh +``` + +Optionally, to support tab-completion: +``` +curl -so readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/readx.source.sh +``` + +* Mark the main script as executable: `chmod +x panssh` +* Run it as just `./panssh` to see further instructions. ### Minimal installation @@ -76,7 +89,7 @@ chmod +x panssh sudo mv panssh /usr/local/bin/ ``` -Run it as just `panssh` to see further information. +Run it as just `panssh` to see further instructions. ### Optional: tab-completion of local site and environment names diff --git a/lando/.lando.panssh.yml b/lando/.lando.panssh.yml index 13b519e..055c6d3 100644 --- a/lando/.lando.panssh.yml +++ b/lando/.lando.panssh.yml @@ -1,15 +1,8 @@ # This file is an optional part of the PanSSH utility. -# It provides service and tooling setup to enable use of PanSSH under Lando. -# See: https://github.com/LastCallMedia/panssh +# It provides service and tooling setup to facilitate use of PanSSH under Lando. -# Installation: -# Place this file in the same location as your application's .lando.yml file. -# Do one of: -# Rename it to lando.local.yml, or merge it into your existing .lando.local.yml file. -# Add .lando.panssh.yml into the postLandoFiles section of your ~/.lando/config.yml -# See: https://docs.lando.dev/landofile/#configuration -# -# Run lando rebuild to apply the changes. +# See: https://github.com/LastCallMedia/panssh +# and: ttps://github.com/LastCallMedia/panssh/lando/README.md # Commands provided: # diff --git a/lando/README.md b/lando/README.md new file mode 100644 index 0000000..c8e22a2 --- /dev/null +++ b/lando/README.md @@ -0,0 +1,30 @@ +# šŸ–„ļø PanSSH under Lando + +This file is an optional part of the [PanSSH](https://github.com/LastCallMedia/panssh) utility. It provides service and tooling setup to facilitate use of PanSSH under Lando. + +## šŸ“¦ Installation + +1. Either clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or download `.lando.panssh.yml` directly: + +``` +curl -so .lando.panssh.yml https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh/lando/.lando.panssh.yml +``` +2. Place `.lando.panssh.yml` in the same location as your application's `.lando.yml` file. + +2. Do one of: + 1. Rename it to `.lando.local.yml`, or merge it into your existing `.lando.local.yml` file, if present. + 2. Add `.lando.panssh.yml` into the `postLandoFiles` section of your `~/.lando/config.yml` (recommended). + + āš™ļø In either case, see: https://docs.lando.dev/landofile/#configuration for further information. You may need to create `~/.lando/config.yml` if it is currently missing or empty. + +3. Run `lando rebuild` to apply the changes. This will download and install the relevant components into your Lando application: + * PanSSH scripts. + * Supporting OS packages. + * PanSSH sites configuration file (runs Terminus). + +## Commands provided: + +* `lando panssh ` — starts PanSSH for the specified site and environment. +* `lando pssh` — starts PanSSH for the related site and environment name matching your current git branch. + +--- diff --git a/panssh b/panssh index 8365b21..f796397 100755 --- a/panssh +++ b/panssh @@ -11,7 +11,7 @@ # License: MIT ############################################################################### -readonly PANSSH_VERSION="PanSSH 1.2.0" +readonly PANSSH_VERSION="PanSSH 1.2.1" # --- Configuration --- readonly SSH_PORT="2222" @@ -61,6 +61,9 @@ if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then echo -e " View a file: .vw " echo -e " Edit a file: .ed " echo -e " Toggle automatic directory listing: .ls\n" + echo -e "To create or update your available sites configuration, run:" + echo -e " terminus site:list --format=csv --fields=name,id > $SITES_FILE\n" + exit 1 fi From 65b1e721366231adc8cafd9bf759acc3acaebfc6 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Mon, 21 Jul 2025 20:43:54 +0100 Subject: [PATCH 04/12] Added work-around for SSH file transfer under Lando. Improved handling of SSH known_hosts - now kept separately from main known_hosts. Refactored SSH options - now using a single set for both ssh and scp/rsync. Further updates to main and Lando README. --- README.md | 8 +++++--- lando/README.md | 6 +++++- panssh | 36 +++++++++++++++++++++--------------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6ff725b..971a9e4 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,13 @@ panssh site.env ``` ### Non-Interactive + +##### From command-line: ``` -# From command-line: panssh site.env "command1; command2; ..." - -# From stdin: +``` +##### From stdin: +``` panssh site.env < script.sh echo "commands" | panssh site.env ``` diff --git a/lando/README.md b/lando/README.md index c8e22a2..f9318ef 100644 --- a/lando/README.md +++ b/lando/README.md @@ -1,6 +1,10 @@ # šŸ–„ļø PanSSH under Lando -This file is an optional part of the [PanSSH](https://github.com/LastCallMedia/panssh) utility. It provides service and tooling setup to facilitate use of PanSSH under Lando. +This file is an optional part of the [PanSSH](https://github.com/LastCallMedia/panssh) utility. It provides service and tooling setup to facilitate use of PanSSH under [Lando](https://lando.dev/). + +## āœ… Requirements + +- A Lando application built using the [Lando Pantheon Plugin](https://docs.lando.dev/plugins/pantheon/index.html) or other configuration which meets [PanSSH requirements](https://github.com/LastCallMedia/panssh/blob/main/README.md#-requirements). ## šŸ“¦ Installation diff --git a/panssh b/panssh index f796397..f2dad46 100755 --- a/panssh +++ b/panssh @@ -26,30 +26,29 @@ readonly LOCAL_USER=$(whoami) readonly REMOTE_HOME="/tmp/panssh-home.$LOCAL_USER.$LOCAL_HOST" readonly REMOTE_ENV="HOME=\"$REMOTE_HOME\" XDG_CACHE_HOME=\"$REMOTE_HOME/.cache\"" -readonly SSH_SOCKET="$TEMP_DIR/ssh.socket" -readonly SSH_GENERAL_OPTIONS=" \ - -o LogLevel=info \ +readonly SSH_OPTIONS=" \ + -o ConnectTimeout=30 \ -o ControlMaster=auto \ - -o ControlPath=$SSH_SOCKET \ + -o ControlPath=$TEMP_DIR/ssh.socket \ -o ControlPersist=5m \ - -o StrictHostKeyChecking=no \ - -o ConnectTimeout=30" -readonly SSH_OPTIONS="-p $SSH_PORT $SSH_GENERAL_OPTIONS" -readonly SCP_OPTIONS="-q -P $SSH_PORT $SSH_GENERAL_OPTIONS" + -o LogLevel=info \ + -o Port=$SSH_PORT \ + -o StrictHostKeyChecking=accept-new \ + -o UserKnownHostsFile=$STORAGE_DIR/known_hosts" # Location of this script. readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# --- Load SITE_IDs from config --- +# Load SITE_IDs from config --- if [[ ! -f "$SITES_FILE" ]]; then - echo "🟔 Sites file not found at: $SITES_FILE" - echo - echo "To create it, run:" - echo " terminus site:list --format=csv --fields=name,id > $SITES_FILE" + echo -e "\n🟔 Sites file not found at: $SITES_FILE" + echo -e "\nTo create it, run:" + [[ "$LANDO" == "ON" ]] && echo -e " lando ssh" + echo -e " terminus site:list --format=csv --fields=name,id > $SITES_FILE\n" exit 1 fi -# --- Parse command-line, get site.env argument --- +# Parse command-line, get site.env argument. if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then echo -e "\n$PANSSH_VERSION" echo -e "\nUsage examples:\n" @@ -62,6 +61,7 @@ if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then echo -e " Edit a file: .ed " echo -e " Toggle automatic directory listing: .ls\n" echo -e "To create or update your available sites configuration, run:" + [[ "$LANDO" == "ON" ]] && echo -e " lando ssh" echo -e " terminus site:list --format=csv --fields=name,id > $SITES_FILE\n" exit 1 @@ -134,7 +134,13 @@ fi # --- File transfer between local and host --- transfer_file() { - scp $SCP_OPTIONS "$1" "$2" + # For some reason, scp fails under Lando, reason unknown. + # Until resolved, we use rsync as a workaround. + if [[ $LANDO != "ON" ]]; then + scp -q $SSH_OPTIONS "$1" "$2" + else + rsync -qe "ssh $SSH_OPTIONS" $1 $2 + fi return $? } From 9772362684bf9137b1d210dfc9e964bcea7b8c82 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Tue, 22 Jul 2025 10:34:27 +0100 Subject: [PATCH 05/12] Lando support - tidy-up. Download paths in .lando.local.yml pointed to lando branch on github. Change them back before final merge! --- lando/.lando.panssh.yml | 15 ++++++++------- panssh | 12 ++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lando/.lando.panssh.yml b/lando/.lando.panssh.yml index 055c6d3..bb5198e 100644 --- a/lando/.lando.panssh.yml +++ b/lando/.lando.panssh.yml @@ -2,7 +2,7 @@ # It provides service and tooling setup to facilitate use of PanSSH under Lando. # See: https://github.com/LastCallMedia/panssh -# and: ttps://github.com/LastCallMedia/panssh/lando/README.md +# and: https://github.com/LastCallMedia/panssh/lando/README.md # Commands provided: # @@ -12,20 +12,21 @@ services: appserver: build_as_root: - # - echo "Installing panssh..." + - echo "PanSSH installation..." # PanSSH main script. - - curl -so /usr/local/bin/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh + - curl -so /usr/local/bin/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh - chmod +x /usr/local/bin/panssh # PanSSH readx utility script. - mkdir -p /usr/local/lib/panssh/ - - curl -so /usr/local/lib/panssh/readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/readx.source.sh - # Install Bash tab-completion support. - - apt-get -qqq update && apt-get -qqq install bash-completion + - curl -so /usr/local/lib/panssh/readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/readx.source.sh + # Bash tab-completion support and basic editor. + - apt-get -qqq update && apt-get -qqq install bash-completion nano # Bash-completion support for the panssh command (and other commands available via `lando ssh`). # For this to work, we would have to either uncomment /etc/bash.bashrc "enable bash completion" section, # or include the equivalent in ~/.bashrc or some other suitable location. # - mkdir -p /usr/local/share/bash-completion/completions - # - curl -so /usr/share/bash-completion/completions/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/bash-completion/panssh + # - curl -so /usr/share/bash-completion/completions/panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/bash-completion/panssh + - echo "PanSSH installation complete." build: # Run terminus (if available) to create the .panssh.sites file. - hash terminus && terminus auth:login && terminus site:list --format=csv --fields=name,id > $HOME/.panssh.sites diff --git a/panssh b/panssh index f2dad46..48916e8 100755 --- a/panssh +++ b/panssh @@ -114,7 +114,7 @@ cleanup() { local status=$? history -a ssh -O exit $SSH_OPTIONS "$USER@$HOST" 2>/dev/null - rm -r "$TEMP_DIR" #> /dev/null 2>&1 + rm -r "$TEMP_DIR" echo -e "\nConnection to $SITE_NAME.$ENV_ID closed." >&2 exit $status } @@ -134,12 +134,12 @@ fi # --- File transfer between local and host --- transfer_file() { - # For some reason, scp fails under Lando, reason unknown. + # Standard scp fails under Lando, reason unknown. # Until resolved, we use rsync as a workaround. if [[ $LANDO != "ON" ]]; then - scp -q $SSH_OPTIONS "$1" "$2" + scp -q $SSH_OPTIONS "$1" "$2" else - rsync -qe "ssh $SSH_OPTIONS" $1 $2 + rsync -qe "ssh $SSH_OPTIONS" "$1" "$2" fi return $? } @@ -186,7 +186,7 @@ edit_file() { elif [[ "$action" == "ed" ]] && ! ssh_exec "[ -w \"$remote_path\" ]"; then echo "🚫 File is not writable." else - can_edit=1 + can_edit=1 download_needed=1 fi elif [[ "$action" == "ed" ]]; then @@ -197,7 +197,7 @@ edit_file() { elif ! ssh_exec "[ -w \"$dirname\" ]"; then echo "🚫 Directory is not writable." else - can_edit=1 + can_edit=1 fi else echo "🚫 File not found." From 68cd16ef146ad8295793e421a1cfc2b1a8f3c2c1 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Tue, 22 Jul 2025 14:26:01 +0100 Subject: [PATCH 06/12] Pointed raw.githubusercontent.com URLs to lando branch rather than `latest` tag. --- README.md | 4 ++-- lando/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 971a9e4..a106c44 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,12 @@ Either clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or The main script: ``` -curl -so panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh +curl -so panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh ``` Optionally, to support tab-completion: ``` -curl -so readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/readx.source.sh +curl -so readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/readx.source.sh ``` * Mark the main script as executable: `chmod +x panssh` diff --git a/lando/README.md b/lando/README.md index f9318ef..127df5b 100644 --- a/lando/README.md +++ b/lando/README.md @@ -11,7 +11,7 @@ This file is an optional part of the [PanSSH](https://github.com/LastCallMedia/p 1. Either clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or download `.lando.panssh.yml` directly: ``` -curl -so .lando.panssh.yml https://raw.githubusercontent.com/LastCallMedia/panssh/refs/tags/latest/panssh/lando/.lando.panssh.yml +curl -so .lando.panssh.yml https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh/lando/.lando.panssh.yml ``` 2. Place `.lando.panssh.yml` in the same location as your application's `.lando.yml` file. From 1f0db6e91c09ae7a067bed43f8b7d488295928b2 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Tue, 22 Jul 2025 19:49:42 +0100 Subject: [PATCH 07/12] Updated main and Lando README. Corrected download path for .lando.panssh.yml. --- README.md | 28 ++++++++++++++++++---------- lando/README.md | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a106c44..d3f407c 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,19 @@ PanSSH emulates an interactive SSH connection to a Pantheon site's application e You can do almost everything that you could if a standard SSH login were available, and it looks and feels near identical. +### Quick start + +##### Download and run the main script + +``` +curl -so panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh +chmod +x panssh +./panssh +``` +Further instructions will then be displayed. + ### Recent changes -* 1.2.1: Added `.lando.panssh.yml`, which provides easy setup of PanSSH in a [Lando](https://lando.dev/) project. +* 1.2.1: Added [.lando.panssh.yml](https://github.com/LastCallMedia/panssh/blob/lando/lando/.lando.panssh.yml), which provides [easy setup](https://github.com/LastCallMedia/panssh/blob/lando/lando/README.md) of PanSSH in a [Lando](https://lando.dev/) project. * 1.2.0: Tab-completion is now included, on supporting systems: * Local site and environment names. * Remote directory and file names. @@ -67,23 +78,20 @@ echo "commands" | panssh site.env ### No installation -Either clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or download the individual files: +The only required file is the `panssh` script. -The main script: -``` -curl -so panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh -``` +##### Download just the main script -Optionally, to support tab-completion: ``` -curl -so readx.source.sh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/readx.source.sh +curl -so panssh https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh ``` - -* Mark the main script as executable: `chmod +x panssh` +* Mark the script as executable: `chmod +x panssh` * Run it as just `./panssh` to see further instructions. ### Minimal installation +Clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or download and unzip the [zip archive](https://github.com/LastCallMedia/panssh/archive/refs/heads/lando.zip). + Mark the main `panssh` script as executable, then copy or move it to any suitable directory that's included in your PATH. ``` diff --git a/lando/README.md b/lando/README.md index 127df5b..3ab7d1d 100644 --- a/lando/README.md +++ b/lando/README.md @@ -11,7 +11,7 @@ This file is an optional part of the [PanSSH](https://github.com/LastCallMedia/p 1. Either clone the [PanSSH repository](https://github.com/LastCallMedia/panssh) or download `.lando.panssh.yml` directly: ``` -curl -so .lando.panssh.yml https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/panssh/lando/.lando.panssh.yml +curl -so .lando.panssh.yml https://raw.githubusercontent.com/LastCallMedia/panssh/refs/heads/lando/lando/.lando.panssh.yml ``` 2. Place `.lando.panssh.yml` in the same location as your application's `.lando.yml` file. From b51284d79a6fb42a1c443832317a5c965b5c1dda Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Tue, 22 Jul 2025 20:03:29 +0100 Subject: [PATCH 08/12] Improved usage display, now shows ./panssh if script was run as ./panssh. --- panssh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/panssh b/panssh index 48916e8..712f1df 100755 --- a/panssh +++ b/panssh @@ -41,6 +41,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Load SITE_IDs from config --- if [[ ! -f "$SITES_FILE" ]]; then + echo -e "\n$PANSSH_VERSION" echo -e "\n🟔 Sites file not found at: $SITES_FILE" echo -e "\nTo create it, run:" [[ "$LANDO" == "ON" ]] && echo -e " lando ssh" @@ -50,12 +51,13 @@ fi # Parse command-line, get site.env argument. if [[ ! "$1" =~ ^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)$ ]]; then + [[ "$0" == "./panssh" ]] && panssh="$0" || panssh="panssh" echo -e "\n$PANSSH_VERSION" echo -e "\nUsage examples:\n" - echo -e " panssh site.env" - echo -e " panssh site.env \"commands\"" - echo -e " panssh site.env < script.sh" - echo -e " echo \"commands\" | panssh site.env" + echo -e " $panssh site.env" + echo -e " $panssh site.env \"commands\"" + echo -e " $panssh site.env < script.sh" + echo -e " echo \"commands\" | $panssh site.env" echo -e "\nSpecial commands:\n" echo -e " View a file: .vw " echo -e " Edit a file: .ed " From c160ec0a053981e7da3cbe9cf4e8364f7d35ebf4 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Tue, 22 Jul 2025 20:24:51 +0100 Subject: [PATCH 09/12] Added remote WP_CLI_CACHE_DIR setting. Updated README. --- README.md | 5 +++-- panssh | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d3f407c..d63ece5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# šŸ–„ļø PanSSH – Pantheon Interactive SSH Session Emulator +# šŸ–„ļø PanSSH +## An emulated SSH login for Pantheon sites PanSSH emulates an interactive SSH connection to a Pantheon site's application environment using only their available (limited) SSH service. It provides command history, local editing of remote files and an emulated current working directory. -You can do almost everything that you could if a standard SSH login were available, and it looks and feels near identical. +You can do almost everything that you could if a standard SSH login were available, and it looks and feels so familiar that you may not notice the difference. ### Quick start diff --git a/panssh b/panssh index 712f1df..c964e19 100755 --- a/panssh +++ b/panssh @@ -24,7 +24,7 @@ mkdir -p -m 700 "$STORAGE_DIR" "$TEMP_DIR" readonly LOCAL_HOST=$(hostname -s) readonly LOCAL_USER=$(whoami) readonly REMOTE_HOME="/tmp/panssh-home.$LOCAL_USER.$LOCAL_HOST" -readonly REMOTE_ENV="HOME=\"$REMOTE_HOME\" XDG_CACHE_HOME=\"$REMOTE_HOME/.cache\"" +readonly REMOTE_ENV="HOME=\"$REMOTE_HOME\" XDG_CACHE_HOME=\"$REMOTE_HOME/.cache\" WP_CLI_CACHE_DIR=\"XDG_CACHE_HOME/wp-cli\"" readonly SSH_OPTIONS=" \ -o ConnectTimeout=30 \ From 30b8f15fdd422018c4d7c0cc5739831e1d4033e6 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Tue, 22 Jul 2025 20:27:19 +0100 Subject: [PATCH 10/12] Corrected remote WP_CLI_CACHE_DIR setting. --- panssh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panssh b/panssh index c964e19..c7f51fb 100755 --- a/panssh +++ b/panssh @@ -24,7 +24,7 @@ mkdir -p -m 700 "$STORAGE_DIR" "$TEMP_DIR" readonly LOCAL_HOST=$(hostname -s) readonly LOCAL_USER=$(whoami) readonly REMOTE_HOME="/tmp/panssh-home.$LOCAL_USER.$LOCAL_HOST" -readonly REMOTE_ENV="HOME=\"$REMOTE_HOME\" XDG_CACHE_HOME=\"$REMOTE_HOME/.cache\" WP_CLI_CACHE_DIR=\"XDG_CACHE_HOME/wp-cli\"" +readonly REMOTE_ENV="HOME=\"$REMOTE_HOME\" XDG_CACHE_HOME=\"$REMOTE_HOME/.cache\" WP_CLI_CACHE_DIR=\"$REMOTE_HOME/.cache/wp-cli\"" readonly SSH_OPTIONS=" \ -o ConnectTimeout=30 \ From fde0479a72b818378321828a411835ac7d304156 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Thu, 24 Jul 2025 16:15:17 +0100 Subject: [PATCH 11/12] Refactoring and added handling for some special cases. --- README.md | 2 +- panssh | 65 ++++++++++++++++++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index d63ece5..75433c7 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Copy the `panssh` completion script from `bash-completion/` to the `bash-complet * For recent Ubuntu distributions, you can probably use `/usr/local/share/bash-completion/completions/` * For MacOS, maybe `/opt/homebrew/etc/bash_completion.d/` or `usr/local/etc/bash_completion.d`, depending on your system. -Test tab-completion by entering `panssh ` then pressing the tab key. +Test tab-completion by entering `panssh ` then pressing the tab key (create a sites configuration file first). ### Optional: tab-completion of remote directory and file names diff --git a/panssh b/panssh index c7f51fb..9a154e4 100755 --- a/panssh +++ b/panssh @@ -85,30 +85,34 @@ readonly HOST="appserver.$ENV_ID.$SITE_ID.drush.in" # --- SSH wrapper --- ssh_exec() { local cmd=$(printf '%q' "$1") - ssh $SSH_OPTIONS "$USER@$HOST" "echo $cmd | bash; exit \${PIPESTATUS[1]}" - return $? + ssh $SSH_OPTIONS "$USER@$HOST" "echo $cmd | bash" + # Don't add anything here without saving and returning correct status. } # --- SSH wrapper with current directory tracking --- ssh_exec_track() { local cmd="$1" - local marker="___PANSSH_CWD___" - local status=0 - - while IFS= read -r line; do - if [[ "$line" == "$marker "* ]]; then - local meta="${line#"$marker "}" - status="${meta%%,*}" - current_dir="${meta#*,}" - else - echo "$line" - fi + local cwd_marker="PANSSH_status_cwd:" + local line + + # Run the command and print its output until we hit our marker. + while IFS= read -r line && [[ "$line" != "$cwd_marker"* ]]; do + echo "$line" done < <( - ssh $SSH_OPTIONS "$USER@$HOST" \ - "echo $(printf '%q' "$cmd; echo $marker \$?,\$(pwd)") | bash" + ssh_exec "$cmd; echo \"$cwd_marker\$?|\$(pwd)\"" ) - return "$status" + # Extract status code and working directory from the final line. + local status_cwd="${line#"$cwd_marker"}" + + # Check for failure - can happen due to syntax error, etc. + if [[ -z "$status_cwd" ]]; then + return 2 + fi + + # Set remote directory and return status code. + REMOTE_CWD="${status_cwd#*|}" + return "${status_cwd%%|*}" } # --- Clean up and close SSH connection --- @@ -130,7 +134,7 @@ if [[ $# -gt 1 ]] || ! [ -t 0 ]; then else cmd=$(cat) fi - ssh_exec "export $REMOTE_ENV; $cmd" + ssh_exec "$REMOTE_ENV; $cmd" exit $? fi @@ -296,9 +300,10 @@ export HISTCONTROL=ignorespace:ignoredups:erasedups history -r # Set up initial state. -auto_ls=0 +REMOTE_CWD="/code" +current_dir="$REMOTE_CWD" current_status=0 -current_dir="/code" +auto_ls=0 # Basic environment checks. if ssh_exec "cd $current_dir"; then @@ -341,7 +346,10 @@ while true; do [[ -z "$cmd" ]] && continue # Handle `exit`. - [[ "$cmd" == "exit" ]] && break + if [[ "$cmd" =~ ^exit([[:space:]]+(.*))?$ ]]; then + current_status="${BASH_REMATCH[2]:-0}" + break; + fi # Handle `.ed` (edit) and `.vw` (view) if [[ "$cmd" =~ ^\.(ed|vw)[[:space:]]+([^[:space:]]+)(.*)$ ]]; then @@ -364,15 +372,18 @@ while true; do continue fi - # Run the command with our environment and correct directory. - current_dir_before="$current_dir" - ssh_exec_track "export $REMOTE_ENV; cd \"$current_dir\" && $cmd" + # Run the command with our environment, current directory and correct initial value for $? + ssh_exec_track "export $REMOTE_ENV; cd \"$current_dir\" && bash -c \"exit $current_status\"; $cmd" current_status=$? - # Automatic directory listing if enabled and directory was changed. - if [[ "$auto_ls" -eq 1 ]] \ - && [[ "$current_dir" != "$current_dir_before" ]]; then - ssh_exec "cd \"$current_dir\" && ls -pC --group-directories-first" + # Check whether the command changed the current directory. + if [[ -n "$REMOTE_CWD" && "$REMOTE_CWD" != "$current_dir" ]]; then + current_dir="$REMOTE_CWD" + + # Automatic directory listing if enabled. + if [[ "$auto_ls" -eq 1 ]]; then + ssh_exec "cd \"$current_dir\" && ls -pC --group-directories-first" + fi fi done From 6ec02290a8e66e1a5a7fb482c24db58640a7a987 Mon Sep 17 00:00:00 2001 From: Andy Inman Date: Thu, 24 Jul 2025 20:05:36 +0100 Subject: [PATCH 12/12] Added syntax check before executing interactive commands. Avoids weird error messages if syntax is wrong. --- panssh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/panssh b/panssh index 9a154e4..a013b48 100755 --- a/panssh +++ b/panssh @@ -89,23 +89,29 @@ ssh_exec() { # Don't add anything here without saving and returning correct status. } -# --- SSH wrapper with current directory tracking --- +# --- SSH wrapper with syntax check, status and current directory tracking --- ssh_exec_track() { - local cmd="$1" local cwd_marker="PANSSH_status_cwd:" - local line + + # Build command parts. + local pre_cmd="$1" + local cmd="$2" + local clean_cmd="${cmd%;}" + local post_cmd="echo \"$cwd_marker\$?|\$(pwd)\"" # Run the command and print its output until we hit our marker. + local line while IFS= read -r line && [[ "$line" != "$cwd_marker"* ]]; do echo "$line" done < <( - ssh_exec "$cmd; echo \"$cwd_marker\$?|\$(pwd)\"" + # Run syntax check first, then full command if syntax is valid. + ssh_exec "bash -n -c '$cmd' && bash -c '$pre_cmd; $clean_cmd; $post_cmd'" ) # Extract status code and working directory from the final line. local status_cwd="${line#"$cwd_marker"}" - # Check for failure - can happen due to syntax error, etc. + # Check for failure - can happen due to syntax error (status 2), etc. if [[ -z "$status_cwd" ]]; then return 2 fi @@ -373,7 +379,7 @@ while true; do fi # Run the command with our environment, current directory and correct initial value for $? - ssh_exec_track "export $REMOTE_ENV; cd \"$current_dir\" && bash -c \"exit $current_status\"; $cmd" + ssh_exec_track "export $REMOTE_ENV; cd \"$current_dir\" && bash -c \"exit $current_status\"" "$cmd" current_status=$? # Check whether the command changed the current directory.