Making Statamic play nicely
We’ve been using Statamic for some of our lightweight content sites here at fffunction recently.
Whilst being a super-easy setup and dev environment for everyone, its flat file CMS throws up some challenges when real content is added and needs to be version controlled with git.
What are we trying to do?
Once a Statamic site is publicly hosted and editable by content authors, we want to cleverly manage what content can be seen locally for development, and what gets deployed back to the staging or production server.
As this is not a big issue while in development, the assumption made here is that during the early stages of a Statamic build all files are included into the repo.
In late stages of build we want to exclude some areas of Statamic from the local dev repos, so that the server-generated content is treated as canonical.
We knew that we wanted it both ways - to allow any source code changes to be pushed to the server, yet preserve any changes to content and still easily pull down changes locally.
Simply doing a gitignore on these files felt messy, and a merge strategy too blunt.
Here comes the science bit
Using a POST webhook, we’re using git on the server to commit any new files that have been created through the Statamic admin panel, do a pull, then use this saved commit hash to checkout and override any changes to our protected files.
First, check if there’s any CMS-generated content to add, using:git status --porcelain
This will return nothing if the repo’s clean.
If the repo is dirty, then add all files and commmit:git add . git commit -am "Adds all new files"
Store the hash for resetting:git log --pretty=format:'%h' -n 1
Now pull the latest from origin:git pull
We then checkout the earlier stored commit hash with the paths of the content we want to keep unchanged, in this case _content/ and _config/users/git checkout [stored commit hash] _content/ _config/users/
This means we’ll never have merge conflicts on the server, and can always rely on our CMS-generated content staying safe.
At this stage we can check if the repo is dirty again, and exit if there’s nothing to push.
If we didn’t exit, we commit the protected content:git commit -am "Resetting content"
And push back to origingit push origin [branch]
Now, if there’s anything new or changed on the server, it’s pushed back to the repo so we can get the live content in our local environments.
Avoid the infinite loop
As we’re using webhooks to listen to commits to origin, we test to make sure the commit payload sent to the webhook is not from the server user, otherwise we’d create an infinite loop of commits and hooks.
This process was inspired in part by Gareth Redfern’s post An Update To My Git Workflow.
Much help and input from fffunction’s own Dan Reeves, especially on the checkout and stored commit hash bits.