public
Last active

Minimal Race-free Deployment

  • Download Gist
2dirs/deploy.sh
Shell
1 2 3 4 5 6 7
#!/bin/sh
# deploy.sh
N="`readlink \"$1\"`"
mv -T "$1.stage" "$1"
ln -s "$N" "$1.stage"
rm -rf "$N"
cp -aH "$1" "$N"
2dirs/initialize-deployable.sh
Shell
1 2 3 4 5 6 7
#!/bin/sh
# initialize-deployable.sh
mkdir "$1.d"
mv "$1" "$1.d/1"
ln -s "$1.d/1" "$1"
cp -aH "$1" "$1.d/2"
ln -s "$1.d/2" "$1.stage"
3dirs/deploy.sh
Shell
1 2 3 4 5 6 7 8
#!/bin/sh
# deploy.sh
N="`readlink \"$1.prev\"`"
cp -PT "$1" "$1.prev"
mv -T "$1.stage" "$1"
ln -s "$N" "$1.stage"
rm -rf "$N"
cp -aH "$1" "$N"
3dirs/initialize-deployable.sh
Shell
1 2 3 4 5 6 7 8
#!/bin/sh
# initialize-deployable.sh
mkdir "$1.d"
mv "$1" "$1.d/1"
ln -s "$1.d/1" "$1"
cp -aH "$1" "$1.d/2"
ln -s "$1.d/2" "$1.stage"
ln -s "$1.d/3" "$1.prev"
3dirs/rollback.sh
Shell
1 2 3 4 5 6 7 8 9 10 11
#!/bin/sh
# rollback.sh
[ ! -e "$1.prev" ] && echo "Can't roll back." && exit 1
N="`readlink \"$1.stage\"`"
cp -PT "$1" "$1.stage"
mv -T "$1.prev" "$1"
ln -s "$N" "$1.prev"
rm -rf "$N"
# Keep current as stage on rollback? If no, uncomment:
# mkdir "`readlink \"$1.stg\"`"
# cp -alH "$1" "$1.stg"

Good idea, but your rsyncs could probably use a --delete and copying the old version into the new directory before syncing is better done with the "-l" flag (to hardlink instead of duplicating them).

If somebody is looking for a more sophisticated method with the same idea for deploying Rails or PHP applications, it's better to check Capistrano. We are using it to deploy Magento changes as well.

Yep, check cap or vlad the deployer. you don't need to reinvent the wheel.

The great advantage of this technique is that it is easily documented, and someone looking at the directory full of "web-20121009" and similar directories with a symlink "web" -> "web-2012-10-23" should be able to figure out what is going on.

It's simple, it works, and can be reverse-engineered relatively easily. The only caveat is the reliance on GNU "ln", but the atomic replacement of symlinks is only covering the few milliseconds it takes to replace the symlink. If your website users are that sensitive to documents changing from underneath them, you'll need a different solution such as switching VMs on the fly.

#!/bin/sh
ACTION=$1
NAME=$2

if [ $# -ne 2 ]
then
    echo "Usage: deploy-tool.sh <action> <dir> "
    echo "Actions:"
    echo "  init        Initialize an existig directory <dir> for deployment"
    echo "  deploy      Deploy the content <dir>.stage to <dir> also back up"
    echo "              to <dir>.prev"
    echo "  rollback    Rollback from <dir>.prev"
    exit 1
fi

if [ ! -d $NAME ]; then
    echo "Directory '"${NAME}"' does not exist."
    exit 1
fi

if [ $ACTION = "init" ]; then

    echo "Initialize dir '"${NAME}"'"
    mkdir "${NAME}.d"
    mv "${NAME}" "${NAME}.d/1"
    ln -s "${NAME}.d/1" "${NAME}"
    cp -aH "${NAME}" "${NAME}.d/2"
    ln -s "${NAME}.d/2" "${NAME}.stage"
    ln -s "${NAME}.d/3" "${NAME}.prev"

elif [ $ACTION = "deploy" ]; then

    echo "Backup from '"${NAME}"' to '"${NAME}".prev'"
    N="`readlink \"${NAME}.prev\"`"
    cp -PT "${NAME}" "${NAME}.prev"
    echo "Deploy from '"${NAME}".stage' to '"${NAME}"'"
    mv -T "${NAME}.stage" "${NAME}"
    ln -s "$N" "${NAME}.stage"
    rm -rf "$N"
    cp -aH "${NAME}" "$N"

elif [ $ACTION = "rollback" ]; then

    if [ ! -e "$NAME.prev" ]; then
        echo "Cannot roll back."
        exit 1
    fi

    N="`readlink \"$NAME.stage\"`"
    cp -PT "$NAME" "$NAME.stage"
    echo "Roll back from '"${NAME}".prev'"
    mv -T "$NAME.prev" "$NAME"
    ln -s "$N" "$NAME.prev"
    rm -rf "$N" 

else
    echo "Unknown action."
    exit 1
fi

echo "Done."

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.