Azure DevOps Server repository backup - CloudCommandos/JohnChan GitHub Wiki

This script git clones all repositories in the specified DevOps Server Collections. Trigger this script with a cron-job that runs daily. Make sure to supply the username and apikey. You will also need an SSH key that is accepted by the DevOps Server.

#!/bin/bash
# Make sure the user who runs this script has an SSH key accepted by the DevOps Server

# Settings
devops_server_url="http://your_devops_server_ip_or_domain"
devops_server_username="<Your DevOps Server user account name>"
devops_server_user_apikey="<Get from DevOps Server user account>"
backup_to="./backup_root_folder_name"

## number of latest backup folders to retain (does not include 'latest' folder)
num_retain=3

## Declare collections in which repositories need backup
collections=( CAD CAP8 GIS Others RMS )

curdir=$(pwd)
timenow=$(date '+%Y-%m-%d')
echo -e "Folder name will be ${timenow}"
for collection_name in ${collections[@]}; do
  # Get list of projects
  echo -e "\n- Getting all projects in collection ${collection_name}"
  collection_name=${collection_name// /%20}
  collection_name=${collection_name%\"}
  collection_name=${collection_name#\"}
  projects_query=$(curl -s -u ${devops_server_username}:${devops_server_user_apikey} "${devops_server_url}/${collection_name}/_apis/projects?api-version=5.1")

  num_projects=$(echo $projects_query | jq ".value | length")
  if [ "$num_projects" != "" ]; then
    echo -e "- Collection ${collection_name} has ${num_projects} projects"
    for (( idx=0; idx<$num_projects; idx++ )); do
      # Get list of repositories in project
      project_name=$(echo $projects_query | jq ".value[$idx].name")
      echo -e "\n-- Getting all repositories in project ${project_name}"
      project_name=${project_name// /%20}
      project_name=${project_name%\"}
      project_name=${project_name#\"}
      repositories_query=$(curl -s -u ${devops_server_username}:${devops_server_user_apikey} "${devops_server_url}/${collection_name}/${project_name}/_apis/git/repositories?api-version=5.1")

      num_repositories=$(echo $repositories_query | jq ".value | length")
      if [ "$num_repositories" != "" ]; then
        echo -e "-- Project ${project_name} has ${num_repositories} repositories"
        for (( idx2=0; idx2<$num_repositories; idx2++ )); do
          sshUrl=$(echo $repositories_query | jq -r ".value[$idx2].sshUrl")
          repository_name=$(echo $repositories_query | jq -r ".value[$idx2].name")
          repository_name=${repository_name// /%20}
          repository_name=${repository_name%\"}
          repository_name=${repository_name#\"}
          if [ "$sshUrl" != "" ]; then
            mkdir -p "${curdir}/${backup_to}/latest/${collection_name}/${project_name}"
            if [ ! -d "${curdir}/${backup_to}/latest/${collection_name}/${project_name}/${repository_name}/.git" ]; then
              echo -e "--- Cloning repository ${repository_name}"
              cd "${curdir}/${backup_to}/latest/${collection_name}/${project_name}"
              git clone $sshUrl "${repository_name}"
            else
              echo -e "--- Pulling repository ${repository_name}"
              cd "${curdir}/${backup_to}/latest/${collection_name}/${project_name}/${repository_name}"
              git pull $sshUrl
            fi
          fi
        done
      else
        echo -e "-- Project ${project_name} has no repository"
      fi
    done
  else
    echo -e "- Collection ${collection_name} has no project"
  fi
done

# Copy/Merge latest folder into timestamped folder
echo -e "\nSyncing from latest to timestamped folder"
mkdir -p "${curdir}/${backup_to}/${timenow}"
rsync -pavW --progress --delete "${curdir}/${backup_to}/latest/*" "${curdir}/${backup_to}/${timenow}"

echo -e "\nRetain up to ${num_retain} backups"
cd "${curdir}"
folders=( $(ls "$backup_to") )
# Remove 'latest' folder from selection
folders=( "${folders[@]/latest}" )
# sort in descending order
IFS=$'\n' sorted_folders=($(sort -r <<<"${folders[*]}")); unset IFS
# retain specified number of backups
cd "${curdir}/${backup_to}"
for i in ${!sorted_folders[@]}; do
  if (( $i >= $num_retain )); then
    echo -e "- Removing ${sorted_folders[$i]}"
    rm -rf "${sorted_folders[$i]}"
  fi
done

echo -e "\nDone!"