ignore "file not found"
[infrastructure.git] / scripts / git-mirror.sh
1 #!/bin/bash
2
3 ADMINS="Talip-Baris.Metin@inria.fr Thierry.Parmentelat@inria.fr"
4 MIRROR_GIT="git://git.planet-lab.org"
5 MASTER_GIT="/git"
6 LOCAL_MIRROR_DIR="/git-mirror"
7 QUIET=0
8 FAILED=0
9 FAILED_CMDS=""
10 NOTIFIED_FILE="NOTIFIED_ADMINS"
11 RUNNING_FILE=$LOCAL_MIRROR_DIR/RUNNING_MIRROR
12
13 function msg () {
14     if [ $QUIET -eq 0 ] ; then
15         echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "$@"
16     fi
17 }
18
19 function error () {
20     SUBJECT=$1
21     MSG=$2
22     CHECK_FILE=$3/$NOTIFIED_FILE
23
24     if [ -f $CHECK_FILE ];  then
25         return
26     fi
27
28     for admin in $ADMINS; do
29         echo -e "$MSG" | mail -s "$SUBJECT" $admin
30     done
31     touch $CHECK_FILE
32 }
33
34 function run () {
35     if [ $QUIET -eq 1 ]; then
36         COMMAND="$1 &> /dev/null"
37     else
38         COMMAND="$1"
39         msg $COMMAND
40     fi
41     REPO=$2
42
43     pushd ${REPO} > /dev/null
44     eval $COMMAND
45     if [ $? -ne 0 ]; then
46         FAILED=1
47         FAILED_CMDS="$FAILED_CMDS\n$COMMAND" 
48     fi
49     popd > /dev/null
50 }
51
52 function run_ignore_errors () {
53     OLD_FAILED=$FAILED
54     OLD_FAILED_CMDS=$FAILED_CMDS
55     run "$1" "$2"
56     FAILED=$OLD_FAILED
57     FAILED_CMDS=$OLD_FAILED_CMDS
58 }
59
60 function merge_all_branches () {
61     NAME=$1
62     REMOTE=$2
63     REPO_DIR=$3
64
65     pushd $REPO_DIR > /dev/null
66     BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
67     popd > /dev/null
68
69     run "git checkout master" ${REPO_DIR}
70     run "git merge --ff $REMOTE/master" ${REPO_DIR}
71     for BRANCH in $BRANCHES ; do
72         run_ignore_errors "git branch $BRANCH $REMOTE/$BRANCH" ${REPO_DIR}
73         run "git checkout $BRANCH" ${REPO_DIR}
74         run "git merge --ff $REMOTE/$BRANCH" ${REPO_DIR}
75     done
76 }
77
78 function push_all_branches () {
79     NAME=$1
80     REMOTE=$2
81     REPO_DIR=$3
82
83     pushd $REPO_DIR > /dev/null
84     BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
85     popd > /dev/null
86
87     run "git push $REMOTE master:master" ${REPO_DIR}
88     for BRANCH in $BRANCHES ; do
89         run "git push $REMOTE $BRANCH:$BRANCH" ${REPO_DIR}
90     done
91 }
92
93 function mirror () {
94     for arg in "$@" ; do
95         FAILED=0   # reset previous failure if any
96
97         NAME=$(basename ${arg} | sed s/.git$//g)
98         GIT_NAME=${NAME}.git
99         REPO_DIR=${LOCAL_MIRROR_DIR}/${NAME}
100         MIRROR_REPO=${MIRROR_GIT}/${GIT_NAME}
101         MASTER_REPO=${MASTER_GIT}/${GIT_NAME}
102
103
104         # if there is no remote repository it may be that we only have
105         # the repository locally and don't need to mirror
106         git ls-remote $MIRROR_REPO &> /dev/null
107         if [ $? -eq 0 ]; then
108             if [ -d ${REPO_DIR} ]; then
109                 msg "pulling from ${NAME}"
110                 run "git fetch origin --tags" ${REPO_DIR}
111                 run "git fetch origin" ${REPO_DIR}
112                 merge_all_branches $NAME origin $REPO_DIR
113                 if [ $? -ne 0 ]; then
114                     error "git-mirror.sh failed" "Can not fetch from ${MASTER_REPO}" $REPO_DIR
115                 fi
116             else
117                 msg "mirroring ${NAME} for the first time"
118                 run "git clone ${MIRROR_REPO}" ${LOCAL_MIRROR_DIR}
119                 run "git remote add local_master ${MASTER_REPO}" ${REPO_DIR}
120             fi
121
122             msg "pushing ${NAME} to local master"
123             run "git fetch local_master --tags" ${REPO_DIR}
124             run "git fetch local_master" ${REPO_DIR}
125             merge_all_branches $NAME local_master $REPO_DIR
126             if [ $FAILED -ne 0 ]; then
127                 pushd ${REPO_DIR} > /dev/null
128                 STATUS_OUT=$(git status)
129                 popd > /dev/null                
130                 error "git-mirror.sh failed on ${MIRROR_REPO}" "STATUS:\n$STATUS_OUT \n\n------------\n FAILED COMMANDS:\n$FAILED_CMDS" $REPO_DIR
131             else
132                 run "git push --tags local_master" ${REPO_DIR}
133                 push_all_branches $NAME local_master $REPO_DIR
134
135                 # success, remove previous check file if any
136                 CHECK_FILE=$REPO_DIR/$NOTIFIED_FILE
137                 rm -f $CHECK_FILE
138             fi
139         fi
140     done
141 }
142
143
144 function failure () {
145     msg "Received signal - cleaning up $RUNNING_FILE and aborting"
146     rm -f $RUNNING_FILE
147     exit 1
148 }
149
150 while getopts ":hq" opt
151 do
152   case $opt in
153       q)
154           QUIET=1
155           break
156           ;;
157       h)
158           echo "Usage: $0 [-q] REPONAME*"
159           exit 1
160           ;;
161       \?)
162           echo "Invalid option: -$OPTARG" >&2
163           ;;
164   esac
165 done
166
167
168 # is the stamp older than an hour ? 
169 # in minutes
170 GRACE=60
171 is_old=$(find $RUNNING_FILE -mmin +$GRACE 2> /dev/null)
172 if [ -n "$is_old" ] ; then
173     msg "$RUNNING_FILE is older than $GRACE minutes - removing"
174     rm -f $RUNNING_FILE
175 fi
176
177 if [ -f $RUNNING_FILE ] ; then
178     msg "Found $RUNNING_FILE - another git-mirror seems to be running. Aborting... " 
179     exit 1
180 fi
181
182 trap failure ERR INT
183
184 shift $((OPTIND-1))
185 date > $RUNNING_FILE
186 mirror $@
187 rm -f $RUNNING_FILE