This website and its contents are deployed to my VPS via git. The repository is pushed to a production remote and uses hooks to run some basic scripts that organize the incoming content. This is almost CI/CD, but not quite; regardless, git can be a powerful tool to build simple deployment pipelines to remote servers.
Local and Remote Repo Setup
First, we want to set up the basic development repository with a git init
. I always have a remote backup to push to on Codeberg, weather I plan on publishing the code publically or not, for some redundancy. Run a git remote add origin git@example.com/me/my-remote
, and of course replace this example repo URL with your real one.
Next, we're going to set up our production remote. Connect to your target server, and navigate to the /var/www
directory, then run mkdir my-website-bare
. In this post, I'm going to be using a static website as an example, and it's common for production websites to live in this directory. Once, there run git init --bare
to initialize a bare git repository. You'll notice that the bare repo does not have a .git
directory, but instead will show all the contents of what would be in that directory at the root.
❯ ls -l
total 12
drwxr-xr-x. 1 brandon brandon 0 Apr 30 15:11 branches
-rw-r--r--. 1 brandon brandon 66 Apr 30 15:11 config
-rw-r--r--. 1 brandon brandon 73 Apr 30 15:11 description
-rw-r--r--. 1 brandon brandon 21 Apr 30 15:11 HEAD
drwxr-xr-x. 1 brandon brandon 506 Apr 30 15:11 hooks
drwxr-xr-x. 1 brandon brandon 14 Apr 30 15:11 info
drwxr-xr-x. 1 brandon brandon 16 Apr 30 15:11 objects
drwxr-xr-x. 1 brandon brandon 18 Apr 30 15:11 refs
Now, let's add this bare repository as a remote to our local repo by running the following:
git remote add prod me@myserver:/var/www/my-website-bare
For the purposes of this little walk through, I won't get into the weeds of each of these files and directories; the only directory we're concerned with here is hooks
. Git hooks are scripts that Git will execute when certain events happen in the repository. They are flexible and very useful when automating workflows that involve Git. I use them in my website's production workflow, and in some of my development workflows.
The post-receive
Hook
The workflow we're aiming for here is as follows:
- Push to the production repository from your local repo.
- When the prod remote receives the new code it will distribute it where it needs to be.
- Once the code has been updated, we can also rebuild the static site or run any commands needed to update the website with the changes.
In your bare repo, create a file called post-receive
in the hooks
directory, and write the following in it:
#!/bin/bash
# Unset the git environment variables
local_desc=$(git describe)
foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C /var/www/my-website-prod/.git describe)
# navigate to the production repo and execute
# a git pull on the prod main branch
cd /var/www/my-website-prod || exit
git --git-dir=/var/www/my-website-prod/.git pull prod main
The first thing you'll notice here, is we have another website repository at /var/www/my-website-prod
. This is where the website will actually live, and where your web server will be pointing to your static files. Go ahead and navigate there and run the following:
git clone /var/www/my-website-bare /var/www/my-website-prod
This will clone your bare repo into a new, un-bare repo located in my-website-prod
.
So to break down our post-receive
hook: Lines 4, 5, and 6 will unset the git environment variables from our bare repository, and then set them to the env variables from our production repository at /var/www/my-website-prod
. Next, we will navigate to the prod repo and execute a
git pull prod main
, to pull the main branch of the production remote to our production repository, deploying the code.
Now, when you run a git push prod main
in your local repository, it will push to the bare remote, the post-receive
hook will trigger, and will then pull the code from the bare repo, to the production repo.
Conclusion
The flexibility of Git hooks are very useful. I throw in other script calls to run tests and rebuild my website on a single push, allowing me to deploy changes to the website without ever having to ssh
to my production server, creating a small and efficient CI/CD pipeline for my
personal website. Easy peasy, right? Have fun!
Top comments (0)