Many protocols are available at alwaysdata for accessing your accounts remotely: SSH, FTP, WebDAV, etc. However, it seems that deploying an app in production suffers from a lack of flexibility. So, we decided to take care of that.
Please, alwaysdata, tell us about deployment again!
Some things are simpler than others for a developer. Unfortunately, deployment isn’t one of those simple things, being stuck between legacy FTP delivery, file copy over SFTP, patch release remotely, etc. Each project has its constraints and choices.
Solutions like Heroku learned early on that the one who produces the code shouldn’t have to take care of the deployment processes. The one producing the code just has to stay focused on their job: producing good code. However, not everybody has a DevOp available to release the last production build. In this situation, you only have the option of doing it yourself.
Because alwaysdata is designed by developers, we took some time to brainstorm about the best way to deploy in production. We’ve got some ideas for an integrated tool that will allow you to deploy automatically, but our priority goes to other technologies like HTTP/2.
So, we want to give you a solution that makes your life easier. Like you, we prefer to deliver our code in one single command in our terminal. So, we took a look at how to deploy using Git.
Git push
. Period.
Here is our proposal, which is based on another solution that uses containers: Simply git push
your production branch, and it’s done.
TL;DR: If you want to understand how it works (because you’re curious), continue reading. If you just want to use it, you can go to our GitHub repository alwaysdata/autodeploy-git-hook where a short README will guide you through the configuration process.
Let’s dive in. You will need:
- a SSH access to set it
- your codebase versioned using Git
- known git-hooks principles (but we will explain them later)
eggs, flour, milk
On the Server-side
Let’s begin by creating a repository in your alwaysdata account (feel free to adapt paths; nothing here is mandatory):
1 2 3 | $ ssh my-account@ssh-my-account.alwaysdata.net $ git init --bare ~/my-project.git |
We create it bare flavor. This means it will contain only the config and versioning parts. We don’t need more here for deployment.
Then, we will create a script for the post-receive
hook. Like its name suggests, it will handle the push
action to the repository and will be executed right after by taking a reference to the pushed branch. We will use it to test the branch (namely a production
branch) and deploy its content to a directory that contains the codebase served by our Apache instance. On your server, create a file named ~/my-project.git/hooks/post-receive
with the following content:
1 2 3 4 5 6 7 8 9 10 11 | #!/bin/bash while read oldrev newrev ref do if [[ $ref = refs/heads/production ]]; then echo "Deploying 'production' branch to production" git --work-tree=$HOME/www/my-project --git-dir=$(dirname $PWD) checkout --force production exit 0 fi done |
We need to test whether the pushed branch is the production
one and then checkout its content to the ~/www/my-project
directory (the path you defined in your cockpit’s website settings section), which contains the codebase served by your HTTP server.
Don’t forget to make the script executable with chmod +x ~/my-project.git/hooks/post-receive
.
On the Local-side
Back to your local repository in your development device. Go to your project directory, and add the alwaysdata deploy repository:
1 2 3 | $ cd my-project $ git remote add deploy my-account@ssh-my-account.alwaysdata.net:~/my-project.git |
It’s done! You now just need to push your production
branch to the deploy
remote, and deployment will be finished:
1 2 3 4 5 6 7 8 9 10 11 | $ git push deploy production Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 291 bytes | 291.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Deploying 'production' branch to production remote: Already on 'production' To my-account@ssh-my-account.alwaysdata.net:~/my-project.git 39949b8..86bd6c7 production -> production |
On your production
branch, you just have to merge the version that you want to deploy, and push it to the deploy
remote. That’s all.
Cool, but I don’t only deploy static files
You’re right. We just take a simple use-case, and we know that you often need to do more things when deploying.
You probably want to restart your Apache instance to take changes into account. For this use-case, simply add this instruction to the post-receive
script, just after the git checkout
branch:
1 2 | curl --basic --user "API_KEY account=ACCOUNT:" --data '' --request POST https://api.alwaysdata.com/v1/site/SITE_ID/restart/ |
It performs a cURL request that will call the alwaysdata API, using your API key, your account name, and the site ID you want to perform an action on; it also restarts the instance transparently.
Here again, nothing more to do than pushing your production
branch.
More!
Want more? Alright!
You probably need more things to automate your whole release process. So we open-sourced the code for this hook to the alwaysdata/autodeploy-git-hook GitHub repository. The script is configurable using shell variables at the top of the file. You just have to make small changes to adapt it to your workflow.
We are aware that this script is far from being complete. You probably have some migrations to do on your databases or some static files generation to perform when you update your app. Give us some feedback at issues or submit pull-requests so we can improve it together.
Desn’t work ‘out of the box’ with a free account.
You need to add #!/bin/bash to post-receive.
Be sure to toggle plain code before copying and pasting or you’ll end with not valid –options.
If yoy push your code, you’ll get a “remote: fatal: Not a git repository” error.
It works with the autodeploy git hook, thank you!
Hi Rafa,
sorry for the pain you’ve encountered in trying. You’ve spotted two bugs in the rendering of the post that I have corrected then.
Thanks a lot for your attention and your support :)
Yeah, happy to read that :D !
Thanks, usefull article.
I do the git remote add and the git push from a gitlab deployment task.
And on server side I’ve completed the hook script to get the gitlab build artifacts, to install pip requirements, to do the DB migration and to launch collectstatic.
Regards.
Glad to see it has helped you! Your use-case is definitely what I had in mind when I wrote this blog post, thanks for sharing your feedback :)
Doesn’t work at all in my case… :/
I am still getting the annoying “Not a git repository” error.
Yet I copied my code from the plain code version, and even rewrote it by hand just to be sure…
Managed to fix it using the code from the GitHub repo, still have no idea what I did wrong when I followed your article :/
Same as Louis-Marie, post-receive example won’t work (Fatal, not a git repository) althoug I did toggle code view on the code example before copy/paste.
Code from the git repo works.
Would be nice to update the post.
Thank you both!
I’ve just fixed the code in the article, not sure about what happened before, but this version should be OK.
Enjoy!
It’s a great post on how to automate the deploy process. However I have some questions.
when it comes to deploy Django app I have to run some commands. they are:
1. pip install ‑r requirements.txt (to install dependency)
But for that I have to first activate virtual environment: source env/bin/activate
2. python manage.py collectstatic (To collect the static resources)
3. python manage.py migrate (Migrating database4)
Has anybody worked with the hook integrating these commands? If so please help me out.
I would appreciate it.
NB: I could integrate the commands in the post-receive hook, But how can I enable virtual environment?
Hi there! Thanks for your feedback
About your question: venv activation is only a matter of interactive setup in your terminal when working with Python. It mostly only updates your environment to first access to the commands inside the venv (
python
,pip
, etc). You can use those commands from the venv in your post-receive hook directly. E.g., if your venv is located at$HOME/venv/my-app-ven
, you can call$HOME/venv/my-app-venv/bin/python
or$HOME/venv/my-app-venv/bin/pip
in your script and all python related tasks will use the venv stuff.Have a nice deployment!