Git for Websites / post-receive / Separation of Te

2019-05-01 07:38发布

问题:

I'm using Git to manage my website's source code and deployment, and currently have the test and live sites running on the same box. Following this resource http://toroid.org/ams/git-website-howto originally, I came up with the following post-receive hook script to differentiate between pushes to my live site and pushes to my test site:

while read ref
do
  #echo "Ref updated:"
  #echo $ref -- would print something like example at top of file
  result=`echo $ref | gawk -F' ' '{ print $3 }'`
  if [ $result != "" ]; then
    echo "Branch found: "
    echo $result
    case $result in
      refs/heads/master )
        git --work-tree=c:/temp/BLAH checkout -f master
        echo "Updated master"
        ;;
      refs/heads/testbranch )
        git --work-tree=c:/temp/BLAH2 checkout -f testbranch
        echo "Updated testbranch"
        ;;
      * )
        echo "No update known for $result"
        ;;
    esac
  fi
done
echo "Post-receive updates complete"

However, I have doubts that this is actually safe :) I'm by no means a Git expert, but I am guessing that Git probably keeps track of the current checked-out branch head, and this approach probably has the potential to confuse it to no end.

So a few questions:

  1. IS this safe?

  2. Would a better approach be to have my base repository be the test site repository (with corresponding working directory), and then have that repository push changes to a new live site repository, which has a corresponding working directory to the live site base? This would also allow me to move the production to a different server and keep the deployment chain intact.

  3. Is there something I'm missing? Is there a different, clean way to differentiate between test and production deployments when using Git for managing websites?

As an additional note in light of Vi's answer, is there a good way to do this that would handle deletions without mucking with the file system much?

Thank you, -Walt

PS - The script I came up with for the multiple repos (and am using unless I hear better) is as follows:

sitename=`basename \`pwd\``

while read ref
do
  #echo "Ref updated:"
  #echo $ref -- would print something like example at top of file
  result=`echo $ref | gawk -F' ' '{ print $3 }'`
  if [ $result != "" ]; then
    echo "Branch found: "
    echo $result
    case $result in
      refs/heads/master )
        git checkout -q -f master
        if [ $? -eq 0 ]; then
            echo "Test Site checked out properly"
        else
            echo "Failed to checkout test site!"
        fi
        ;;
      refs/heads/live-site )
        git push -q ../Live/$sitename live-site:master
        if [ $? -eq 0 ]; then
            echo "Live Site received updates properly"
        else
            echo "Failed to push updates to Live Site"
        fi
        ;;
      * )
        echo "No update known for $result"
        ;;
    esac
  fi
done
echo "Post-receive updates complete"

And then the repo in ../Live/$sitename (these are "bare" repos with working trees added after init) has the basic post-receive:

git checkout -f
if [ $? -eq 0 ]; then
    echo "Live site `basename \`pwd\`` checked out successfully"
else
    echo "Live site failed to checkout"
fi

回答1:

Would a better approach be to have my base repository be the test site repository (with corresponding working directory), and then have that repository push changes to a new live site repository, which has a corresponding working directory to the live site base? This would also allow me to move the production to a different server and keep the deployment chain intact.

Yes definitely. It's a very rare occasion you want your test site hosted right next to your production site. It's dangerous and unprofessional in almost every regard, not to speak about database corruption, webserver lockups etc.

I do usually have a VM setup for testing purposes. Works very well and I can have it with me on my laptop while travelling.

Using git to deploy your website is a very good idea, there are a lot of other people doing so (e.g. Rob Conery). If you happen to have a live and testing site anyway, you should have seperate branches for them in your repository, set-up as remote-tracking branches on the corresponding server repositories. Your workflow becomes as easy as doing work in your test branch, push it to test, test it, merge to live and push live.

Honestly, don't make it too hard for yourself.



回答2:

Think that both ways will work.

You can also use "git archive master | tar -C c:/temp/BLAH -x" and "git archive live-site | ssh live-site 'tar -C /var/www -x'".

Keeping separate repositories may be useful, but "push inside another push-related hook" looks tricky and I expect it to be slow. Sort of long chain that will be slow and fragile.

May be live site updates should be manually triggered after testing the "testing" version?



回答3:

I also followed the same guide at toroid.org, but I'd like to note that although you started with a bare repository, by adding a working directory, extra handling will most likely be needed. I found that the following hook is useful if you have content that may change dynamically or otherwise and don't want to lose data when using git checkout -f

pre-receive

#!/bin/sh
git add -A
git diff --quiet --cached
if [ $? -gt 0 ]; then
    git commit --quiet -m "autocommit"
    echo "Working Directory was out of sync. Pull to receive updated index."
    exit 1
fi

This will stop a push if there are changes in the remote working directory. Think of it as someone (the web server) making changes but forgetting to commit them. Using checkout with the -f will discard those changes. This hook is a good place to prevent this from happening, but it would be nice if there was also a hook called on the remote server prior to a pull so that you would receive these changes seamlessly.

post-receive

#!/bin/sh
git checkout -f
echo "Working directory synced."

Regarding have two branches, I thought your first solution was more elegant than having to deal with multiple repositories. If you really want to keep your production site isolated, you could use rsync locally which has similar delta patching. I would have a testing and stable branch in the repository with only the testing site as the working directory. When ready to release merge the testing into the stable branch, push, and have a hook looking for commits to stable branch make the call to rsync.