The workflow in which developers work together is essential to the longevity and productivity of the team. Based on my experiences with various types of development setups (some more painful than others), I’d like to share what I feel has emerged as the most efficient setup for our team at Lev Interactive.
So, at its core, what should a development environment accomplish and what features should it have?
- Secure, cloud based Git repositories.
- Ability to work with multiple project types. e.g. node.js, php, rails
- Organized project based ticketing & wiki pages (Lightweight Github-esque interface).
- Workflow & development based on the Branching Model.
- Flexible enough to handle various types of deployments based on the project’s needs. This includes custom build scripts, database migration, and file syncing.
- Optional continuous integration for projects.
- All above components centralized into one place. A happy place where project managers and developers live in peace.
To accomplish all of the above I chose the following tools of the trade. All of which are open source and well maintained.
- Linux (Debian) w/ the essentials apt-get install build-essential libssl git libssl-dev
- GitLab CE and GitLab CI (CI is optional)
And that’s it! Pretty light, eh? With the proper configuration, we’ve really made it work for us.
Bringing it Together
Let’s talk a little about workflow before we get back into the software. Usually for any particular project there are always a few different branches lying around:
- any other specific feature that may be getting worked on.
Notice something weird? No master branch. I found it to be a little redundant, as it usually mirrors the production branch. There are definitely circumstances in which having a master could be useful, but as a company who deals primarily in the web, this is the most Semantic setup for the vast majority of projects.
You’ll also notice something special about the staging and production branch. A git push to origin (origin being our remote GitLab repository) will actually deploy the codebase to the appropriate server. This is possible because of Git’s very lovely feature; hooks. In this case we’re using the post-receive hook which lives on the GitLab server here: /home/git/repositories/<project-name>/<project-name>.git/hooks.
That’s all well and great but what should the post-receive hook actually be?
First we’ll add a basic shell script in the /usr/bin/ named deploy. This way the deploy script will be globally accessible on the server. This script will handle file syncing by making use rsync and its ability to read and interpret a .gitignore file. This will allow for smart syncing so nothing gets overwritten on our destination server. Lastly, we add one line to the post-receive hook:
read oldrev newrev refname
deploy <branch> /var/www/<some-project>/<staging|production|other>/ <server-ip> <server-port> <server-user>
# Plus any other commands to compile code, restart node server, all via SSH...
# This works because the destination server obtains the dev/deployment server's SSH key.
You will see that deploy accepts 5 arguments:
- branch to listen for
- destination directory for the branch export
- ssh IP or resolvable name
- ssh port
- ssh user
Normally you would call the deploy function twice, one listening for staging and one for production. However, you could easily add as many as you’d like for any branch.
Now deploying is as simple as:
::~ git push origin staging
Counting objects: 13, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 564 bytes | 0 bytes/s, done.
Total 6 (delta 5), reused 0 (delta 0)
remote: ### Deploying to staging.
remote: ### Creating temporary checkout of staging.
remote: ### Copying to remote server.
remote: ### Cleaning up.
remote: ### Deployed successfully!
Pretty flexible right? For larger teams it’s also probably best if every developer doesn’t have the permission to push to these servers. The combination of Git and GitLab allows for a very elegant solution for this by using merge requests. Essentially, every user has their own fork of the main project repository. I will let the diagram below explain.
Note: it’s important for the staging and production servers to contain the SSH key for the GitLab server in their authorized_keys file for deployments to work.