Motivation - the web is wrong§
I first came to know of jekyll when I googled for git help and found gitready. After reading the content and solving my initial problem, I was curious enough to have a look behind the scenes at how that particular site was built. At the time I was focussing a lot of effort in how to make things I'd been working on faster using various caching techniques, and gitready helped me realise that this was, while useful and undoubtedly applicable in many circumstances, not actually the best way to solve serving content which is not dynamic. By 'not dynamic' I mean requesting information which is public and changes a lot less often (if at all) than it is requested.
All in all the web is full of sites which are 'dynamic' but in fact do not change from minute to minute, hour to hour, or even (as is the case for this blog) month to month. So why generate content in a dynamic way, why have every visitor that requests a specific page jumping through the same hoops as a previous visitor. The various caching techniques I'd been working on were all more or less based on moving answers as close to the question as possible. A typical dynamic request has several steps, all of which fit this analogy where there is a question and an answer:
- User asks for a page
- Web-server asks php for a page
- PHP asks database for data
- Database answers
- PHP takes data and answers in html
- Web-server answers html
- User gets html
Especially in the case where the user is a search engine - why should all those steps be necessary, why is it even a possibility for a search engine to potentially cause load requesting content that is static, or hasn't changed since it was written - all those months/years ago. It aught to be simply:
- User asks for a page
- Web-server answers html
No problems with databases going offine, no problems with too many php processes killing a server, no load problems - just a user getting exactly what they asked for - some text.
I've used several static site builders since then, including of course jekyll, but I would always end up with the same sort of problems - due to the infrequency of updating them, when the time came to write or edit content, the system would feel a little alien and therefore any problems which required tweaking the engine code would become a roadblock to writing content.
Thus, after a recent spurt of productivity I bring you: Phase.
The install instructions are detailed in the readme, but are quite simple: install cake, install phase, install dependencies, initialize your Site dir, edit your core.php phase settings and start writing.
Phase attempts to clearly separate the written content from the site structure and the application itself. The folder structure is as follows:
- application root
There are a few more folders of course - I left those out for brevity. The highlighted Site dir is where your posts go, the weboot is where your webserver points when you are running locally. The location of the Site folder is configurable so you could move it anywhere, the intention is that this would be a separate repository such that you can version control your content independent of the application code.
Phase is so named because there are several phases to producing a site. You write. you build. and you deploy. I'll briefly cover each of those steps and some relevant highlights.
Phase expects posts to be written with markdown, with an optional YFM block in the head. This is the same as jekyll and therefore posts written for a jekyll install should be laregely interchangeable with Phase.
Phase has no web administrative interface - though it'd be quite easy to write one if you so prefer. The only tool that is provided for writing content, creates a draft posts with a YFM header ready for you to open in whatever editor you like to thrash out a post.
The phase write cli looks like this:
[20:12][andy@work:~/www/apps/phase(develop)]$ Console/cake phase write "my new post" Welcome to CakePHP v2.1.0-dev Console --------------------------------------------------------------- App : phase Path: /home/andy/www/apps/phase/ --------------------------------------------------------------- 2011-12-24-my-new-post.md created [20:12][andy@work:~/www/apps/phase(develop)]$
After running this cli the file /home/andy/www/phase/Site/View/Posts/2011-12-24-my-new-post.md is ready for editing.
There are no planned enhancements for writing content - enhance your editor :)
The build phase is where the effort has gone with phase. I'm quite fond of the h5bp but one thing I dislike is the near impenetrable build process - which in my experience is too difficult to actually use unless you're building a single page application. Automatically implementing known front-end best practices was one of my first objectives when I started to write phase - ensuring that the end result yields the best performance possible.
The build phase crawls your application and generates a static version of the site in the publish folder, but it does more than 'just' this. It:
- Builds only the files/urls actually referenced by your content1
- Compresses all html, css and js content
- Automatically concatenates css and js files reducing http requests
- Css and js filenames are derived from the hash of the content - no risk of stale browser-cache problems
- Warns of local 404s generated during the build process
After the build process is finished, the publish folder is ready for browsing (just point a browser at it) and/or directly uploading to your server.
There are a few omissions from the build process related to css and image processing - mainly because this site doesn't have any images and barely has any css, but I'll get to that probably before the new year.
The deploy phase is very simple, but before describing exactly what it does, let's cover the classic deploy strategy.
On your remote server you have something like this:
/var/www/ src version1 version2 version3 ... version42 mysite.com -> src/version42
apache/nginx/iis is looking at the folder /var/www/mysite.com. When you upload a new version - you upload it outside of where the webserver is looking. After the upload has completed, the symlink for mysite.com is changed to point at the latest version. There are plenty of benefits to doing things this way, but the most important is when after an update and something horrible has gone wrong - it's absolutely trivial to resolve - you just change the symlink to point at the last known good version.
This is exactly and only what the phase deploy cli does. All of the phase commands have their own help and options, the most interesting option for the deploy command is the --dry-run (or -n for short) option which simply dumps the commands to the screen rather than execute them:
[20:40][andy@work:~/www/apps/phase(develop)]$ Console/cake phase deploy -n Welcome to CakePHP v2.1.0-dev Console --------------------------------------------------------------- App : phase Path: /home/andy/www/apps/phase/ --------------------------------------------------------------- Commands to be run: rsync -rv publish/ myserver.com:src/2011-12-24-2040 ssh myserver.com 'rm mysite.com && ln -sf src/2011-12-24-2040 mysite.com' [20:40][andy@work:~/www/apps/phase(develop)]$
As you can see - it will do exactly what is described above when run fo' real.
In keeping with the mantra "release early, release often" - Phase works and fulfils my current needs. I'm quite happy with how it works but it isn't complete or perfect. I'd like to move the code around and essentially pull out all the logic into framework-agnostic libraries and move almost all the code into plugins so that it can be easily integrated into any application. That said - the project can already be installed as a plugin and the most important part - the build shell - should work without any modifications at all. As mentioned in this post I need to add a few things related to css and image processing, this is perhaps the most pressing omission and is likely to be one of the first addressed.
So there you have it - Phase a stati site builder written using cakephp. If you find the project interesting - don't be afraid to fork it and play around.
It does copy the whole contents of Site/webroot to the publish directory - so if you have hidden content, stick it in there and it'll appear in the publish folder after a build ↩