From e34b464f2cd50052e92aa267624eaa0f09098cf4 Mon Sep 17 00:00:00 2001
From: Andre Anjos <andre.anjos@idiap.ch>
Date: Tue, 27 Sep 2016 11:11:36 +0200
Subject: [PATCH] Use filesystem locks to prevent concurrent use of ~/.pypirc
 (fixes #11)

---
 gitlab/after_deploy.sh  |  2 +-
 gitlab/before_deploy.sh |  1 -
 gitlab/deploy.sh        | 14 ++++---
 gitlab/functions.sh     | 81 +++++++++++++++++++++++++++++++++--------
 4 files changed, 75 insertions(+), 23 deletions(-)

diff --git a/gitlab/after_deploy.sh b/gitlab/after_deploy.sh
index 2cdd86c..dc97ec3 100755
--- a/gitlab/after_deploy.sh
+++ b/gitlab/after_deploy.sh
@@ -3,4 +3,4 @@
 
 source $(dirname ${0})/functions.sh
 
-run_cmd rm -rf ${HOME}/.pypirc
+log_info "*** Not yet implemented ***"
diff --git a/gitlab/before_deploy.sh b/gitlab/before_deploy.sh
index 1fd16d3..25fc0ba 100755
--- a/gitlab/before_deploy.sh
+++ b/gitlab/before_deploy.sh
@@ -3,5 +3,4 @@
 
 source $(dirname ${0})/functions.sh
 
-run_cmd rm -f ${HOME}/.pypirc
 run_cmd $(dirname ${0})/before_test.sh
diff --git a/gitlab/deploy.sh b/gitlab/deploy.sh
index fafec8f..1429525 100755
--- a/gitlab/deploy.sh
+++ b/gitlab/deploy.sh
@@ -3,18 +3,20 @@
 
 source $(dirname ${0})/functions.sh
 
-dot_pypirc
+lock_pypirc
 
-setup register --repository staging
-setup check sdist --formats zip upload --repository staging
+setup_deploy register --repository staging
+setup_deploy check sdist --formats zip upload --repository staging
 
 # if that worked, uploads documentation to pythonhosted if any exists
 if [ -d sphinx ]; then
   log_info "Uploading documentation to ${PYPISERVER} on behalf of ${PYPIUSER}..."
-  setup upload_docs --upload-dir sphinx --repository production
+  setup_deploy upload_docs --upload-dir sphinx --repository production
 fi
 
 # if that worked, uploads source package to the production index
 log_info "Uploading package to ${PYPISERVER} on behalf of ${PYPIUSER}..."
-setup register --repository production
-setup check sdist --formats zip upload --repository production
+setup_deploy register --repository production
+setup_deploy check sdist --formats zip upload --repository production
+
+unlock_pypirc
diff --git a/gitlab/functions.sh b/gitlab/functions.sh
index 840be05..3bdf6ab 100644
--- a/gitlab/functions.sh
+++ b/gitlab/functions.sh
@@ -16,12 +16,12 @@ log_info() {
 
 
 log_warn() {
-  echo -e "(`date +%T`) \033[1;35mWarning: ${@}\033[0m"
+  echo -e "(`date +%T`) \033[1;35mWarning: ${@}\033[0m" >&2
 }
 
 
 log_error() {
-  echo -e "(`date +%T`) \033[1;31mError: ${@}\033[0m"
+  echo -e "(`date +%T`) \033[1;31mError: ${@}\033[0m" >&2
 }
 
 
@@ -71,20 +71,29 @@ run_cmd() {
 }
 
 
-# Runs setup.py
-setup() {
-  run_cmd ${PREFIX}/bin/python setup.py ${@}
-}
-
-
 # Prepares ~/.pypirc
-dot_pypirc() {
+lock_pypirc() {
+  local lockfile=/var/tmp/pypirc_lock
   local rc=${HOME}/.pypirc
-  log_info "Creating ${rc}..."
-  if [ -e ${rc} ]; then
-    run_cmd rm -f ${rc}
-  fi
-  cat <<EOT >> ${rc}
+  local maxtries=10
+  local try=0
+  local sleeptime=30 #seconds
+
+  while true; do
+    if [[ ${try} -lt ${maxtries} ]]; then
+      ((try++))
+      if ( set -o noclobber; echo "$$" > "${lockfile}") 2> /dev/null; then
+        log_info "Successfully acquired ${lockfile}"
+        echo $$ > ${lockfile}
+        log_info "trapping on ${lockfile}..."
+        trap 'rm -f "${lockfile}"; exit $?' INT TERM EXIT
+
+        # start: protected code
+        log_info "Creating ${rc}..."
+        if [ -e ${rc} ]; then
+          run_cmd rm -f ${rc}
+        fi
+        cat <<EOT >> ${rc}
 [distutils]
 index-servers =
     production
@@ -100,7 +109,49 @@ repository: ${TESTSERVER}
 username: ${PYPIUSER}
 password: ${PYPIPASS}
 EOT
-  run_cmd chmod 600 ${rc}
+        run_cmd chmod 600 ${rc}
+        # end: protected code
+        break
+      else
+        log_warn "${lockfile} exists, owned by process $(cat $lockfile)"
+        log_info "Will retry after a ${sleeptime} seconds sleep (${try}/${maxtries})"
+        run_cmd sleep ${sleeptime}
+      fi
+    else
+      log_error "I already retried deploying ${try} times. Aborting..."
+      log_error "You can retry this step when less packages are building."
+      exit 1
+    fi
+  done
+}
+
+
+# Cleans ~/.pypirc, if the lock file belongs to us
+unlock_pypirc() {
+  local lockfile=/var/tmp/pypirc_lock
+  local rc=${HOME}/.pypirc
+
+  # untrap if lock belongs to the running process
+  if [[ $(cat ${lockfile}) == $$ ]]; then
+    run_cmd rm -r ${lockfile}
+    run_cmd rm -rf ${rc}
+    log_info "$ trap - INT TERM EXIT"
+    trap - INT TERM EXIT
+  fi
+}
+
+
+# Runs setup.py in a deployment context. If fails, tries to unlock
+# the ${HOME}/.pypirc file lock
+setup_deploy() {
+  log_info "$ ${@}"
+  ${PREFIX}/bin/python setup.py ${@}
+  local status=$?
+  if [ ${status} != 0 ]; then
+    log_error "Command Failed \"${@}\""
+    unlock_pypirc #just tries
+    exit ${status}
+  fi
 }
 
 
-- 
GitLab