-
Notifications
You must be signed in to change notification settings - Fork 0
Deploying To Remote Server Repositories
When working in a staging or production environment that is not Flywheel, it may be useful to create a remote git repo to easily push your changes without using FTP/SFTP clients. This is our preferred way of pushing code changes to servers when we can. (Some servers we can use this method on are servers like factor1.me, ther29.com, or any client hosted on Skywalker - but are not limited to these servers).
Note: for an ongoing client where staging and production live on the same server, it may be useful to use a multiple branch strategy. See the docs on this strategy here.
By single branch git repo, we mean that the master
branch of the remote repo servers up the files that are pushed. There would never be multiple branches at the remote repo and you would always push to the master
branch of the remote repo.
In this example, we will be setting up a new staging environment on ther29.com under a fake project called StoutRules.
The first thing we'd want to do is connect to the server via ssh in your terminal.
Note: this example server has a custom port for SFTP/SSH, 2222 instead of 22
ssh [email protected] -p 2222
Then at the server root level, we will create a folder called repos
if it doesn't already exist. (Some existing servers may also have a folder called repo
these are interchangeable)
mkdir repos
Now we are ready to setup the remote repo.
## create the repo folder
mkdir stoutrules.git
## navigate to the repo
cd stoutrules.git
## initiate a bare git repo
git init --bare
Now we have a bare repo that we can add our hook to.
A git hook allows us to tell git to do something when it is pushed to. Here we will create a hook called a post-receive
hook, that will do something after it gets the new code changes.
## navigate to the hooks folder
cd hooks
## create a post-receive
cat > post-receive
When running the cat
command, you will automaticcaly be entered into the post-receive
file and you can add your content. To save and exit this prompt press CTRL+D
. (You may have to do this twice)
Now we are ready to enter the bin script that handles the file move.
Below is what our bin script would look like in our project. (You can find an example bin script in GistBox, although GistBox will be removed from our systems by December 2017 and we are searching for a replacement system)
#!/bin/sh
git --work-tree=/home/ther29/public_html/wp-content/themes/stoutRules --git-dir=/home/ther29/repos/stoutrules.git checkout -f
Now lets break this down a little bit. #!/bin/sh
tells the server that this is a bin script. You must include this line.
--work-tree=
is where we will want our code changes to end up, this should be a theme folder or in some cases the public_html
folder depending on where you want your files.
--git-dir=
is where the actual git repo lives on the server. This should be the path to where you setup the initial git repo earlier in this documentation.
The last step of this process is to ensure we have the proper permissions set on our script so that git can excute it. We use the chmod
command with an argument of +x
to make it executeable.
chmod +x post-receive
Now we are all set with a remote git repo we can push our changes to.
Now, we need to tell our local git repo that this new remote repo exists so that we can push to it.
You can name your remote repo whatever you like, however as a best practice we use staging
for staging servers and production
for production servers. We will need the path of the remote repo for this next step, so be aware of where you set this up.
From terminal in your local repo:
Reminder: this paticular server we are working with has a port of 2222 instead of 22, so we need to add that to our remote repo url.
git remote add staging ssh://[email protected]:222/home/ther29/repo/stoutrules.git
Now you can verify that it successfully added by checking your remotes.
git remote -v
With the single branch strategy, we will always push to the master
branch on the remote. You can push any local branch you want, so we just need to tell git what to push.
So lets say we want to push our changes on our local develop
branch to staging.
git push staging develop:master
This should successfully push our changes to the staging server. Lets again breakdown what we are doing here, we will add some placeholders so you know exactly whats happening.
git push {remote name} {local branch}:{remote branch}
Again, the {remote branch}
should always be master
in the single branch strategy.
Cool, so we are able to push to a remote repo and leave FTP and SFTP in the past. In some cases you may receive fast-forward errors and your pushes may be rejected. Normally when we are working with git repos this is a big problem but when pushing to the server it may happen a little more frequent and thats okay. As long as you know your local branch is right - you can force push your changes.
Example:
git push staging develop:master --force
In some cases, we may have a production site and a staging site that both live on the same server and we may be working on both simultaneously. We can use a multiple branch system on the remote repo so that when we push changes to the remote on master
it goes live, and when we push to develop
on the remote, it appears on staging. (Good client examples that can benefit from this -but might not actually use this: Tathata Golf, Lifewater, Rule29.com, our own site)
(There is also a good post on Medium on this by our Senior Dev - Eric, which most of the content on this page comes from)
Login to your server via your terminal and lets create a folder for your repo to live in.
## Login to your server
$ ssh [email protected]
## create repo folder
$ mkdir repos
## navigate to created folder
$ cd !$
Awesome. Now lets make an empty git repository inside of /repos/
.
## create and name your repo folder
$ mkdir yourrepo.git
## navigate to your repo folder
$ cd !$
## initialize empty repo
$ git init --bare
Now here is where the fun comes in. We’re going to need to setup whats called a post-receive
hook to tell our repo what to do with the branches once they are received. So lets navigate to our hooks folder. Chances are, there is not a post-receive file so we will have to create one.
## navigate to hooks folder
$ cd hooks
## create post-receive file
$ touch post-receive
Now we’re ready to go… Let’s dive into the script we will be using to do the magic. The first thing we need to do in our script is tell it where our staging and production trees will be living. We are going to assume that our production lives in public_html/
and that staging lives in public_html/staging/
to make it nice and easy. Of course, you can have your environments live where ever you want on your server. Open post-receive
using your editor of choice and lets get this rolling.
#!/bin/sh
GIT_DIR=$PWD
STAGING_TREE=/home/youruser/public_html/staging
PRODUCTION_TREE=/home/youruser/public_html/
GIT_WORK_TREE=
The above section is pretty straight forward. We are telling the script that the git directory, GIT_DIR
lives in the previous working directory, and that the staging and production trees live in their respective spots on our server. Next we need to setup our git work tree, so that the repository knows where to place the files based on branches. So continuing in the same file:
while read oldrev newrev refname
do
GIT_BRANCH=$(git rev-parse --symbolic --abbrev-ref $refname)
if (test "$GIT_BRANCH" = "master" ); then
cd $PRODUCTION_TREE || EXIT
GIT_WORK_TREE=$PWD
fi
if (test "$GIT_BRANCH" = "develop" ); then
cd $STAGING_TREE || EXIT
GIT_WORK_TREE=$PWD
fi
done
What this section of the script does is check the commits and decide where to place them. If the commits exists on master
then it puts it in the production tree, if its in the branch develop
it moves it to the staging tree.
Next we just need to add one more piece to manage our work trees and branches. Continuing in the same file add:
if (test "$GIT_WORK_TREE" != ""); then
cd $GIT_DIR
git --work-tree="$GIT_WORK_TREE" checkout $GIT_BRANCH -f
fi
This just makes sure that the correct branch and work trees are checked out. All together, your script should look something like this:
#!/bin/sh
GIT_DIR=$PWD
STAGING_TREE=/home/youruser/public_html/staging/
PRODUCTION_TREE=/home/youruser/public_html/
GIT_WORK_TREE=
while read oldrev newrev refname
do
GIT_BRANCH=$(git rev-parse --symbolic --abbrev-ref $refname)
if (test "$GIT_BRANCH" = "master" ); then
cd $PRODUCTION_TREE || EXIT
GIT_WORK_TREE=$PWD
fi
if (test "$GIT_BRANCH" = "develop" ); then
cd $STAGING_TREE || EXIT
GIT_WORK_TREE=$PWD
fi
done
if (test "$GIT_WORK_TREE" != ""); then
cd $GIT_DIR
git --work-tree="$GIT_WORK_TREE" checkout $GIT_BRANCH -f
fi
One last step we need to do on our server is set the right permissions for git to be able to execute the script. So in the hooks/
directory simply run chmod +x post-receive
and we’re set.
Now we just need to setup your local repository to have our new server side repository as a remote. Again, we’re going to assume you’re already up and running with a project using git. So let’s add the remote repository, we’re going to name it “server”.
$ git remote add server ssh://[email protected]/home/repos/yourrepo.git
Now, we can test our first push. Let’s push our develop
branch to staging.
$ git push server develop:develop
This command then pushes our local develop
branch to the develop
branch on our remote server/repo which is mapped to our staging environment.
We repeat the same process for production:
$ git push server master:master
## or
$ git push server
And that’s it! You did it. No need for FTP, no need for two remote repositories, your branches push to their respective environments and your dev time just got a little easier and shorter.
Feel free to open an issue if you find mistakes, enhancements, or would like to see some new documentation.