Bash: change git remote from SSH to HTTPS within a given dir The code takes a user input dir path, then looks for git remote url that is still using SSH, and the repo is belong to an fixed Organisation. Then it will convert these repos' git remote url from SSH to HTTPS.

Goal is to let users (on Linux or Mac) effortlessly convert all dir containing the Organisation's code to use HTTPS instead of SSH.

My senior programmer said problems in the code are so many that he has to sit down with me next week to talk through! Please help me to spot problems before embarrassment starts. Thank you 

#!/usr/bin/env bash

# This script loops through all directories within the given path,
# and run a conversion script to convert Git CLI auth type.
GITHUB_USERNAME='Mock-Technology'

converter () {
  # This function will convert origin url from SSH style to HTTPS style, for the
  # git containing local directory

  if [[ -z $GITHUB_USERNAME ]]; then
    GITHUB_USERNAME='Mock-Technology'
  fi

  echo "-- converter starting..."
  cd "$1" || exit
  if [[ ! -f "config" ]]; then
    echo "This directory does not contain Git, exitting..."
    return
  else
    origin_str=$(cat "config")
  fi

  repo_url=$(echo "$origin_str" | sed -Ene's#.*(git@[^[:space:]]*).*#\1#p')
  if [[ -z "$repo_url" ]]; then
    echo "-- ERROR:  Could not identify Repo url."
    if [[ $origin_str == *"https"* ]]; then
      echo "   This repo is already using HTTPS instead of SSH."; else
      echo "   Valid remote URL was not found!"
    fi
    return
  fi
  echo "-- Confirmed: It is using SSH..."

  user_name=$(echo "$repo_url" | sed -Ene's#git@github.com:([^/]*)/(.*).git#\1#p')
  if [[ -z "$user_name" ]]; then
    echo "-- ERROR:  Could not identify User."
    return
  elif [ "$user_name" != "$GITHUB_USERNAME" ]; then
    echo "-- WARNING: The repo does not belong to '$GITHUB_USERNAME', ignoring..."
    return
  fi
  echo "-- User name extracted: $user_name"

  repo_name=$(echo "$repo_url" | sed -Ene's#git@github.com:([^/]*)/(.*).git#\2#p')
  if [[ -z "$repo_name" ]]; then
    echo "-- ERROR:  Could not identify Repo."
    return
  fi
  echo "-- Repo name extracted: $repo_name"

  new_url="https://github.com/$user_name/$repo_name.git"
  echo "-- Changing repo url from: "
  echo "  '$repo_url'"
  echo "      to "
  echo "  '$new_url'"
  echo ""

  change_cmd="git remote set-url origin $new_url"
  eval "$change_cmd"
  echo "-- Success!"

}

validate_path() {
  # this module validates input path against empty string and non-existing path
  if [[ -z $1 ]]; then
    echo "path is empty string, existting..."
    exit
  fi

  if [[ ! -d $1 ]]; then
    echo "path does not exist, exitting..."
    exit
  fi
  echo "-- path validation check passed..."
}

loop_subdirectories() {
  # visits subfolders and try to find any ".git" directory
  # It invokes converter func for each ".git" directory found
  while read -r dir; do
    converter "$dir"
  done &lt;&lt;&lt; "$(find "$1" -name .git -type d)"

}

read -p "Enter path: " path
validate_path "$path"
loop_subdirectories "$path"
Extracting remote urls

Parsing the config file is error-prone. A much better way to extract the information about remotes is using Git commands. This will simplify your script a great deal.

Limitations

Be aware that the script as it is will not be able to convert remotes that were created using URL shorthands. For example, I have this in my global .gitconfig:

[url "git@github.com:"]
    insteadOf = "gh:"


This lets me clone a GitHub repo with git clone gh:user/repo instead of git clone git@github.com:user/repo. The config file will be written using the shorthand instead of the real URL, so your script will not be able to pick up the pattern.

You can easily solve this limitation by using Git commands to extract Git remote URLs.

String transformations

Instead of echo ... | sed ..., it's better to use here-strings:

sed ... &lt;&lt;&lt; ...


But when the text transformations are simple enough, and Bash's parameter expansion can handle it, that's even better. For example, instead of this:


  user_name=$(echo "$repo_url" | sed -Ene's#git@github.com:([^/]*)/(.*).git#\1#p')



This would be better:

# chop off the beginning until the first :
user_name=${repo_url#*:}
# chop off everything after the first /
user_name=${user_name%%/*}


Exiting with error

There are multiple exit points in the validate_path function.
Without parameter, exit will use the exit code of the previous command, and if that was echo, it's likely to be success,
when in fact you probably want to exit with error instead.

Usability

Reading paths inside a script is not user-friendly,
because you cannot use tab-completion.
It would be better to let users pass paths as parameters to the script.