69

I was trying to create a cronjob with a task to do a git pull every minute to keep my production site in sync with my master branch.

The git pull needs to be done by the system user nobody, due to the permissions problem. However it seems that the nobody account is not allowed run commands. So I have to create tasks as the root user.

The crontab entry I tried:

*/1 * * * * su -s /bin/sh nobody -c 'cd ~heilee/www && git pull -q origin master' >> ~/git.log

It doesn't work, and I don't know how to debug it.

Could anyone help?

UPDATE1: the git pull command itself is correct. I can run it without errors.

hobs
  • 18,473
  • 10
  • 83
  • 106
kayue
  • 2,546
  • 3
  • 20
  • 16
  • What happens when you run the command itself in a shell? – Tom Dec 11 '10 at 00:19
  • Do you have a user named `git.log`? – Dustin Dec 11 '10 at 03:29
  • @Tom it does run if I run the command itself. – kayue Dec 11 '10 at 04:30
  • You'll want to update the output to write to the absolute path of the log. The tilde (~) is a relative path to YOUR home directory. I don't think this will fix your problem, but you should have it end with `... >> /var/log/git.log` – brycemcd Dec 12 '10 at 03:41
  • 3
    Because `git pull` automatically runs `git merge` which may fail with conflicts, and leave things in a state that is non-trivial for an automated script to fix, I would highly dis-recommend doing this on any repository that has even a remote chance of anything other than that one job causing updates that may prove to be incompatible. Use `git fetch` instead, and periodically do a manual merge. – twalberg May 13 '13 at 20:27
  • if `git` has to use `ssh`, see [git push via cron](http://stackoverflow.com/q/7994663/4279) – jfs Jun 02 '15 at 23:26
  • GitHub renamed its default branch from `master` to `main` in 2020. If these answers aren't working, make sure you're indeed pulling from a `master` branch and if not, change that value to match your remote branch. – Sam Firke Jul 31 '23 at 15:41

8 Answers8

44

Solution:

*/1 * * * * su -s /bin/sh nobody -c 'cd ~dstrt/www && /usr/local/bin/git pull -q origin master' 
Angelo Fuchs
  • 9,825
  • 1
  • 35
  • 72
kayue
  • 2,546
  • 3
  • 20
  • 16
  • 3
    doesn't work for me on ubuntu 10.04 server. 3 different errors (~, /local/ and -q all cause problems) – hobs Jul 11 '12 at 18:40
  • 12
    `*/1 * * * * su -s /bin/sh nobody -c 'cd /home/dstrt/src/project && /usr/bin/git pull origin master'` works better for me: ~ expansion more reliable, different path to git for my distro, and -q option for git should be after `pull` or not at all, otherwise git barfs. – hobs Jul 12 '12 at 02:17
  • */1 * * * * /bin/sh -c 'cd /path/to/repo && /usr/bin/git pull -q origin master' worked for me. – aaronbauman Aug 13 '14 at 15:55
  • 6
    `*/1 * * * * /bin/sh -c 'cd /home/userName/repo && /usr/bin/git pull -q origin master'` It is ok for me. My OS is Ubuntu 14.04.02. – diguage May 31 '15 at 10:03
  • @kayue - what does the script do step by step? – Valter Ekholm Dec 22 '20 at 16:07
13

While you do need to figure out how to get the update to work in the first place, you'd be far better off using a hook from the upstream to make it go. You can do this simply with curl from a post-commit hook or if you're using github, just use a post-receive hook on their side.

Dustin
  • 89,080
  • 21
  • 111
  • 133
  • A hook is much cleaner than using cron. Hooks are just scripts, so you can have something like `cd /path/to/production && git pull`. – Cameron Skinner Dec 11 '10 at 03:35
  • Can I run post-commit hook as a 'nobody' ? – kayue Dec 11 '10 at 04:31
  • I tried this script (https://gist.github.com/737188) but have permission problem because the use running this script has no permission to the target directory – kayue Dec 11 '10 at 06:00
  • 14
    cron is self-contained and doesn't involve opening a potential security hole through a web service. Why do you think a webhook is better? – aaronbauman Aug 13 '14 at 15:57
  • 2
    A webhook that fires only when changes will a) get the changes deployed faster and b) not leave you with thousands of processes consuming lots of resources on your machine when something goes wrong (even when nothing is changing). – Dustin Aug 14 '14 at 05:47
  • How can you use hooks when you have to deploy to multiple servers? – Eduardo Oct 08 '17 at 05:55
  • 3
    In environment where web servers are auto spinned and you dont even have idea what IP addresses will be - there would be complicate task to create "hooks" even though it would be cleaner, but I vote for cron's simplicity of git pull. – lapkritinis Sep 24 '18 at 15:38
  • Imagine my case, where we have a cluster of multiple NodeJS servers and a Git Server, and we want to keep production code up to date on all those servers from the Git Server, but we can't garantee that when a developer pushes production code to the Git Server that all NodeJS servers are UP. – Etor Madiv Jun 30 '19 at 18:08
8
*/1 * * * * su -s /bin/sh nobody -c 'cd /home/heilee/src/project && /usr/bin/git pull origin master'

This corrects a couple errors that prevented the accepted answer from working on my system (Ubuntu >10.04 server). The key change seems to be the -q after the pull rather than before. You won't notice that your pull isn't working until you tail the /var/log/syslog file or try to run your non-updated production code.

hobs
  • 18,473
  • 10
  • 83
  • 106
6

At the time of writing this answer, all the suggested solutions start with cd'ing into the working directory. Personally I would prefer to use the git-dir argument as so.

git --git-dir=/path/to/project/.git pull

For my own purposes, I'm using this cron to keep my local dev box up to date with what the other developers. Hence I'm using fetch instead of pull

*/15 * * * * git --git-dir=/home/andey/path/.git fetch -a

To simplify the current accepted solution, using the git-dir argument

*/1 * * * * su -s /bin/sh nobody -c '/usr/local/bin/git --git-dir=~dstrt/www pull origin master'
Andrew Wei
  • 2,020
  • 1
  • 17
  • 28
6

Starting from Git version 1.8.5 (Q4 2013) you can use the -C argument to easily accomplish this. Then you don't need to cd into the Git project directory but can instead specify it as part of the Git command like this:

*/15 * * * * git -C /home/me/gitprojectdir pull
John
  • 349
  • 4
  • 3
4
#!/bin/bash
cd /home/your_folder/your_folder && /usr/bin/git pull git@bitbucket.org:your_user/your_file.git

that is has been using by me and worked

  • @YudaPrawira, you are confusing `&` with `&&` — [further reading](https://stackoverflow.com/a/26770612/884640) – floer32 May 23 '18 at 23:00
3

I create a small script to deal with it.Then can use the by command crontab

crontab -e
0 2 * * * cd /root && ./gitpull.sh > /root/log/cron.log  2>&1 &

Here are the gitpull.sh:

#!/bin/bash

source /etc/profile
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export TERM=${TERM:-dumb}

#----------------------------------------
# Please set the following variable section
# Please set up working directories, use','split
# eg:path="/root/test/path1,/root/test/path2"
path=""
#----------------------------------------

# Do not edit the following section

# Check if user is root
[ $(id -u) != "0" ] && { echo "${CFAILURE}Error: You must run this script as root.${CEND}"; exit 1; } 2>&1

# Check if directory path exists
if [[ "${path}" = "" ]]; then 
    echo "${CFAILURE}Error: You must set the correct directory path.Exit.${CEND}" 2>&1
    exit 1
fi

# Check if command git exists
if ! [ -x "$(command -v git)" ]; then
    echo "${CFAILURE}Error: You may not install the git.Exit.${CEND}" 2>&1
    exit 1
fi

# Check where is command git
git_path=`which git`

# Start to deal the set dir
OLD_IFS="$IFS" 
IFS="," 
dir=($path) 
IFS="$OLD_IFS" 

echo "Start to execute this script." 2>&1

for every_dir in ${dir[@]} 
do 
    cd ${every_dir}
    work_dir=`pwd`
    echo "---------------------------------" 2>&1
    echo "Start to deal" ${work_dir} 2>&1
    ${git_path} pull
    echo "---------------------------------" 2>&1
done

echo "All done,thanks for your use." 2>&1

We have to set the work directory

0

Encountered the same thing, and I highly recommend pulling the branch like this:

git fetch origin master:master

So that it pulls origin master and merges into local master regardless on what branch you're in. Doesn't checkout you, doesn't interrupt your work (except you may encounter a git lock, which isn't gonna be long anyway, considering you pull it every half an hour).

Also, the whole string (every working day, every half an hour + every hour):

0,30 * * * 1-5 cd ~/work/project && git fetch origin master:master
0 * * * * 1-5 cd ~/work/project && git prune

If you need to make a monorepo faster, it's a really good thing.