Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Minimal Race-free Deployment
#!/bin/sh
# deploy.sh
N="`readlink \"$1\"`"
mv -T "$1.stage" "$1"
ln -s "$N" "$1.stage"
rm -rf "$N"
cp -aH "$1" "$N"
#!/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"
#!/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"
#!/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"
#!/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"
@ghost

This comment has been minimized.

Show comment Hide comment
@ghost

ghost Oct 22, 2012

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).

ghost commented Oct 22, 2012

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).

@czettnersandor

This comment has been minimized.

Show comment Hide comment
@czettnersandor

czettnersandor Oct 22, 2012

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.

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.

@bitboxer

This comment has been minimized.

Show comment Hide comment
@bitboxer

bitboxer Oct 22, 2012

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

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

@AlexSatrapa

This comment has been minimized.

Show comment Hide comment
@AlexSatrapa

AlexSatrapa Oct 23, 2012

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.

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.

@aggsol

This comment has been minimized.

Show comment Hide comment
@aggsol

aggsol Oct 24, 2012

#!/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."

aggsol commented Oct 24, 2012

#!/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."
@azamat-sharapov

This comment has been minimized.

Show comment Hide comment
@azamat-sharapov

azamat-sharapov Jun 10, 2014

@czettnersandor , @bitboxer , capistrano and such other tools are usually used for deploying form local server to remote server directly. Our team needed automatic deployment from github to staging server, so I had to create my own wheel after hours of research about existing deployment tools.

@czettnersandor , @bitboxer , capistrano and such other tools are usually used for deploying form local server to remote server directly. Our team needed automatic deployment from github to staging server, so I had to create my own wheel after hours of research about existing deployment tools.

@vacri

This comment has been minimized.

Show comment Hide comment
@vacri

vacri Jun 10, 2016

re: the first comment, if you use hardlinks, won't you overwrite files in the 'live' directory when you rsync file changes to the 'stage' directory?

vacri commented Jun 10, 2016

re: the first comment, if you use hardlinks, won't you overwrite files in the 'live' directory when you rsync file changes to the 'stage' directory?

@Jakobud

This comment has been minimized.

Show comment Hide comment
@Jakobud

Jakobud Feb 13, 2017

@ghost That would make it so that you could never take old website versions offline since each new release is dependent on all the old releases via hard links. It would save you hard drive space, yes, but in reality space is cheap and all your actual content (images and other things that take up a lot of space) should be stored and served from a CDN anyways.

Jakobud commented Feb 13, 2017

@ghost That would make it so that you could never take old website versions offline since each new release is dependent on all the old releases via hard links. It would save you hard drive space, yes, but in reality space is cheap and all your actual content (images and other things that take up a lot of space) should be stored and served from a CDN anyways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment