How I Moved My Blog From Octopress to Sculpin

After the last software update of my local machine, I decided that it was finally time to tick off that ToDo task "Move blog to Sculpin." This task was added at least a year ago, and I kept putting it off. This time it will be a last time I bang my head against the wall trying to make that Ruby crap working, just to be able to publish a new blog post. I have nothing against Ruby (and there are a lot of great tools in that language), but I don't develop in that language, so trying to fix all those annoying issues takes a lot of time. I would rather get frustrated by something I will be comfortable fixing.

Installation, generation, and basics

Started my journey with reading Sculping documentation. You can read whole documentation if you want (was useful for solving other issues), or you can start quickly with it.

First, you start by installing the Sculpin itself:

 curl -O https://download.sculpin.io/sculpin.phar
 chmod +x sculpin.phar

 mv sculpin.phar /usr/local/bin/sculpin

This will provide the sculpin command which can be used for generating the static content, and other tasks.

The blog skeleton is provided as separate repository:

git clone https://github.com/sculpin/sculpin-blog-skeleton.git blog

It contains an app directory for all the logic for generating the blog.

The source directory contains the raw content of the blog (layouts, posts, views, etc.).

Run the sculpin install command to install the dependencies of the blog.

After all those steps are done run the sculpin generate command to generate the contents of the blog:

sculpin generate --watch --server

# or

sculpin generate --watch --server --port=4000

The watch flag re-generates the content on file changes, and port flag changes the port the generated content is available on (the default one was taken by a different project).

If you left the skeleton as is, there should be a few example posts. Creating a new post is easy, you only have to create a new file which starts with a date (e.g., 2016-06-20-moving-blog-octopress-to-sculpin.md) at source/_posts.

If you need to generate a production based blog just change the environment to prod:

sculpin generate --server --env=prod

# or

sculpin generate --server --env=prod --port=4000

The blog can be configured through YAML files, and default configuration is at app/config/sculpin_site.yml.

The great thing is that you can provide an environment based configuration, allowing for example to disable analytics or other services for development.

You create an environment based configuration by having a configuration file for that environment (e.g., app/config/sculpin_site_${env}.yml), and importing the default configuration. Example:

imports:
    - sculpin_site.yml

google_analytics_tracking_id: UA-PRODUCTION-372

These are the basics of generating a static blog. If you need more details about what is possible to do with it, you can check out the documentation.

The rest of the blog post is what I done to move my blog from Octopress, differences I run into, and solutions to some problems.

Started by moving all the blog posts (copy-paste of the file), and removing all the fields that are not needed (like layout, date, etc.). It took some time, but allow to fix a few typos, and other trivial stuff.

The first issue I run into was the permalinks. They are set by default to be pretty, but I use global short URLs for my posts.

The meta-data already contains the permalink for a post like permalink: /how-to-use-composer. This won't work as it will create extension-less files, that will be downloaded when you click on them. After some playing around the important part is having the / at the end. The slash at the beginning is not needed and it will mess things up. The permalink: /how-to-use-composer becomes permalink: how-to-use-composer/ (in short just move the slash from beginning to end).

After playing with a lot of strategies of how to generate URLs this is what I decided on for my own taste. Changing the sculpin_content_types.posts.permalink to :slug_filename/ (damn, forgot the slash again). This will change URL to the slug, and if it doesn't exist it will use the filename. This meant I had to rename the posts to use short file names. Actually this is a great strategy, if I name my files just what the slug would be, I will have one less thing to set (date prefix is removed from URL). If I want to change the URL, or need something else than the file name, I can just set the slug for post (e.g., slug: how-to-use-composer). That I like about the slug is that it doesn't need the slash at the end to work. Of course if I need a nested URL it can be achieved by setting the permalink (e.g., permalink: how-to/use-composer/, don't forget the slash at the end). This strategy should provide minimal input, maximum power.

Fixing partial content render

Next issue I run into was that some of the posts render partial content. It's mostly navigation to other articles in the series, but definitely not something I would like to copy paste for each article.

At the first glance it looked like that the include of the files doesn't work. Decided to hack it with providing an array of includes for a post, and including those files on the twig template. My current use is simple for it to work, and if I would need something more complex I could just add a bundle which would take a tag and replace it with contents from the file before rendering.

After a bit more of experimenting found that it's possible to use twig's include(). For it to work the file has to be in the _includes directory (or you could change the settings I guess). It doesn't work if you try to include something what doesn't exist in any of the _includes directories. So, if in Octopress I had {% render_partial series/testing-world-2014-spain.markdown %}, in Sculpin it will be {{ include('series/testing-world-2014-spain.md') }}.

Of course when you get happy about something working, it never works. The file is now included, but it's not converted, outputting markdown content rather than HTML content. Back to the drawing board. After some research it doesn't look like Sculpin has a Twig filter for converting to markdown. For the moment just decided to use twig templates (to just run hash of links through a loop) rather than markdown files.

Front-end - the worst part of moving

Even if I mostly like the current style of my website, I definitely wasn't looking to moving it. I'm just not the fan of doing front-end. And current Octopress version is a mess. The blog is custom styling on SASS, index page is a different (but similar) custom styling on LESS. There are some differences between them, and some other crap which was left from making changes quickly.

Bootstrap

First decision was if I should do it on the Bootstrap. I only need an easy to read, single column layout, meaning I could have done it without Bootstrap, but decided to give it a try. It might make things easier later in case I would need to make changes in the future.

The bootstrap which comes with default skeleton is outdated, so decided to update it. I'm not sure about installing JS/CSS assets through Composer. Another issue with all those package managers is that dependencies come with a bunch of other stuff which is not needed. At the same time you might end with a complicated build step requiring npm and other crap (something I wanted to avoid). At the same time, I definitely didn't feel like working with native CSS, I like variables and nesting in my CSS, thank you.

My decision was to remove the components (they failed to update the versions, and just makes everything too complicated for my needs in this case).

For the Bootstrap I used the generator on the website. If I will ever need to make changes to, it would be fine to do it again using a generated config. For normal styles, added a less file which is being watched by the IDE. For converting Less had to install an npm library (globally).

Favicons

For the favicon generation I used the Real Favicon Generator. Happy with the results it provided (includes customizations, and mostly copy-paste for including the favicons).

Had an issue with favicon not being loaded. Just copy-pasted favicon.ico to the source directory, for those browsers what try to load it automatically from it (FireFox). This removes a lot of 404 requests.

Styling, JS plugins, and other misc changes

Moving all the styling and existing plugins took a while, but it in the end it made everything more readable, so I'm happy about the results.

Not sure about leaving the social plugin which allows to share the content on the social networks. As previously I already made it only to load if page is scrolled (in other words a visitor is reading the post), decided to leave it for a while. Don't think that it makes a big difference, but as it's not getting in the way very much, left it. Still keep thinking about removing social sharing. Even if it's done asynchronously on the first scroll of the page, it still adds a lot of stuff to load (~200KB+) and it makes the page lag, not giving a nice experience. Not a deal breaker, but just feels pointless.

Removed the link for editing blog posts on GitHub. During the years found no use for it, might as well just remove the clutter. At the same time I'm moving my blog repository to be private, making a link to edit a post pointless.

The great thing about moving to private repository would be that I would be able to have article drafts by adding draft: true on posts. That way draft posts would only be shown in dev environment. Octopress might have draft articles too, but as the repository was private it made no sense to use them.

Disabled the tags taxonomy for posts as I don't use them (left the categories on).

Out of curiosity, and to try something new, decided to replace jQuery with Zepto. It has a smaller footprint, and the usage at the moment is pretty low. All I need from it is to find when the document is ready, and add a few events.

For my logo I used to have some fancy CSS, which loads logo as an image, but replace it with an SVG if the browser supports it. Browser support is getting good enough that I just replaced it with the SVG image. This allows me to get rid of libraries like Modernizr, and a bunch of complicated workarounds for stuff that brings no value.

One of the problems I run into was that categories were not working. Actually found that on my current blog, the main links to Technical and Personal categories doesn't work either (facepalm). Well, the big difference is that Octopress doesn't pay attention to the case, it just lowercase the files for categories (it will be /categories/technical). Sculpin does pay attention to the case and leaves it as is (it will be /categories/Technical). I'm fine either way and left it the way Sculpin wants to generate them. Didn't want to change all the categories to lowercase, or muck around trying to find how to output files in lowercase. This might lose some page rank value until it gets reindexed, but the value of that is so small that it's not worth the effort.

The categories pages changes were trivial, it's just a list of links. Changed it to actually be a list, not just bunch of inline links. The only issue was that they are mostly at random order. To fix the issue added sort filter for data.posts_categories in the Twig loop. You would think this would sort them alphabetically, but no, that doesn't work. The order is different, but it still looks like random sort. Might spend some time at some point looking for why this is happening, but at this point it's not valuable, and just left it as is. For specific category pages, I liked the old styling, but wasn't going to spend time moving it over, so just made some small changes to the default Sculpin style.

The category generator from that I could tell is broken. The issue is that if for example I look at the Technical category, it displays the links to the next page, but link to the next page is who knows where. They are generated as normal files (meaning that browser will try to download them), rather as a subdirectory under the category directory. Wasn't much of issue for me as I just threw in the pagination size at 250. Just set the sculpin_pagination.max_per_page in sculpin_kernel.yml to make it global. Once I will reach that limit, I will just increase it (maybe to a thousand). I don't output content on the index pages, and want to keep everything in one page (also allows to use browser based search).

Did the same changes to the archive page, which at the moment is only reachable by URL. The main index provides the same view, more or less.

I removed the tags generator. One of the reasons I disliked the Octopress was the performance. With all the build steps, and other stuff it took a lot of time for changes to happen. I wanted to make my Sculpin re-generation as fast as possible. Don't know if removing it added to regeneration time (as tags are not used at this point -- no changes in processing time), but might as well remove the clutter that I don't need.

The main page which contains about/portfolio -- in Octopress -- is quite a mess. It tries to look similar to the blog, but it's taken from a bought template, and the colors/fonts/etc. are different (slightly). At first was thinking about recreating this hybrid one-column/two-columns layout. In the end decided to just go with the main styling, doing one column, and not banging my head against the wall about this (ship stuff). The end result might not look as crisp, but I like it. It's kind of me, not beautiful, but easy on the eyes, and stomach. This allowed to clean up styling a lot. One of the issues was that with smaller containers those sections that was split into 3 columns would have not looked well. The solution taken was simple - use only 2 columns.

Another thing I'm not yet sure about is: layout being too gray, and headers only being distinguished by their size & font-weight. Might at some point play around with colors (make text more towards dark), and font-family for headers (without using custom fonts).

To some extend it might look like I'm avoiding doing "quality" work. Well partly it's true (in this case), and for a very good reason. Often, my side projects die slowly due to me trying to polish all the corners. I'm trying to fix that by banging myself over the head, and not allowing to go too much into polishing. Just ship, iterate after.

In the end I decided to completely remove Zepto, and just go with vanilla JS. It's just a few event listeners to send GA events, or to load Socialite. When GA is not loaded in its place an empty function is created which does nothing, but is used to avoid getting errors. Happy with this result, shaves off some size from stuff to load, and removes unneeded dependencies.

One issue that I noticed while testing all posts is that if the blog post is very short the social buttons are not loaded (this was in an original blog too). The first fix was checking the document and window height which worked fine for the first time. For the next post it failed as the document height was bigger than window height (not triggering loading manually, leaving it for a scroll event), but the social links where being seen as normal links. Fixed that by using this gist to add the function which checks if element is in viewport. I'm still leaning towards just throwing out this crap called social share buttons; people can use Buffer or different application if they want to share something, this just makes stuff harder.

One thing that I dislike is that Sculpin has no syntax highlighting (one which is not JavaScript based). If having to use JS syntax highlighting, I lead towards Prism. It's not possible to do that as the generated HTML doesn't have language- on the pre tag. Could have spent more time into looking that maybe the parser could provided the different output, or change init script. For the time being this is not a part which provides a lot of value, decided to just go with the default Highlight JS script. Highlight JS doesn't support line numbers (conscious decision), but my opinion is opposite, and I think that line numbers help with reading code snippets. Found a line numbers plugin which works, more or less. Will leave things as is for the time being, but might just change it to something more elegant once it will piss me off. Would also be great if downloaded Highlight JS script had a link as a comment which would choose the same configuration after opening, so I could easily add new languages to highlight. It looks like it's possible to generate highlighted code using this extension for Twig.

All those icon fonts to me always looked like they are an additional crap for most of the sites. At best you use a couple icons, and for that it requires loading a lot of resources. Even worse if multiple icon fonts are used (like on my current Octopress site). After cleanup I needed only a couple social icons. For that reason decided to go with SVG, and by using IcoMoon just get the ones I need. It's easy to add them, just take the file containing all of them, copy-paste the icon markup from provided demo file, and then screw around trying to figure out how to add the styling to them. In the end happy with the result.

Configuration

One of the things that I like about Sculpin is what it has environment based configuration. You can put the most basic stuff inside sculpin_site.yml and when load this file on the environment configuration through imports: [sculpin_site.yml]. Put whatever you need in the environment configuration. This way helps a lot when writing articles, as it doesn't need to load Disqus/GA/SumoMe, or any other service which is only needed in production.

Content generation

Two of the most used commands while making a move were rm -rf output_dev/ to remove everything (needed when making some drastic changes), and sculpin generate --watch --server --port=4000 (to generate content and serve it, port change due to default port being taken).

Image tag

In Octopress, in the beginning, I used {% img %} tags to insert images. This brakes in Sculpin (and don't think there's a lot of point in using a tag like this). Just replace them with simple <img /> tags.

Code snippets with explanations

In Octopress, for some code snippets, I used to add small explanations of them. This was done by starting each code block like this bash Create symbolic places.sqlite link in Windows. Later I stopped doing that, could not see much value in it, and adding explanations for explanations felt like the problem was at not explaining content well. Don't know if Sculpin could do that, but don't care either way; out they go.

404 page

While the application generates 404 page, it does not work on local machine. This makes sense as you need at least a basic server to know what you should serve 404 page if something is not found.

Extending with bundles (and meta tags)

That would be great is if Sculpin updated the sculpin.json automatically (for it to work like Composer does). Rather than updating the JSON file, I could just run something like sculpin require twig/extensions, and it would download the dependency, update the sculpin.json, and figure the version automatically. Following the best practice of Composer -- use CLI -- in other words.

The Sculpin can be extended with bundles. For meta description tag I needed to truncate the text. It could be achieved using slice filter (slice(0, 150)), but decided I want a more elegant solution by using truncate. For that an additional Twig extension is required to work. That bundle actually already comes with Sculpin, it's just not enabled. You have to use the version which comes with Sculpin, as if you want to update the dependency Sculpin has, it starts throwing out the fits.

As the extension is already available, you need to enable it in app/sculpin_kernel.yml. Add the following services definition:

services:
    twig.extension.text:
        class: Twig_Extensions_Extension_Text
        tags:
            - { name: twig.extension }

Twig doesn't have a filter which would remove extra whitespace (in tags, not between them), which made extracting description meta tag harder. Using filters striptags, truncate, replace, trim I was able to get a good enough solution (in my opinion) for extracting the description.

At the same time decided to throw in the meta keywords tag, which just takes categories of the page and outputs them as a string using join(',') twig filter.

The default layout has meta robots tag. In some places it says not to index the page. Not sure that is the point of that, so I just changed it by default to index all the pages.

Redirects

Some of the blog posts use the redirects (those that started on Wordpress & those that had typos in URL). To add redirect functionality used Sculpin Redirect Bundle.

Install it, add to kernel, and create a redirect page in your _includes directory.

Then make changes to redirects: portion of all posts that use it. Rename it to redirect: and for paths add / at the end.

Deploying to S3

As I chose not to keep this in the public GitHub repository, and didn't want to pay for a hosting, my decision was to host in on S3.

For that you need to create a new bucket. Then make a change to s3.conf file. Put the bucket in there and choose to delete the removed files (to keep everything clean).

Next make changes to s3-publish.sh file. I went with removing the existing generated production environment on local machine. As the Sculpin is installed globally change the generate command to remove vendor/bin/ part.

Install the s3cmd, if you use OSX you can do it through brew with brew install s3cmd.

Create an IAM user for publishing to blog, generate the access key, and add the policy that allows to make changes to the bucket for the website.

You can use the following policy (change the <bucket> to real bucket name):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1465221023000",
            "Effect": "Allow",
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::<bucket>",
                "arn:aws:s3:::<bucket>/*"
            ]
        }
    ]
}

I created an .s3/s3cmd.conf file, for s3cmd to be used then publishing to the blog (I avoid setting up the AWS credentials on my machine).

Next enable website hosting on the bucket. Fill in the text inputs with index document (index.html) and error document (404.html).

You might also want to allow public access to all object on the bucket by adding this bucket policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::www.ifdattic.com/*"
            ]
        }
    ]
}

OK, so apparently you can't name your bucket in any way you want, it must be named like your domain. As buckets must be unique between all S3 customers, my suggestion would be to create a bucket with your domain name, even if you're not going to host on S3 - just to be on the safe side.

At the same time decided to move my blog to the www sub-domain. I used to like having a clean URL, but after reading some article about why naked domains are bad, started using the www sub-domain.

Just make sure that (at least until you're making changes to DNS records) you have a short TTL. You can increase it after you're done, but a long time ago I learned that for your own sanity it's better to keep them short (e.g., 300).

For sub-domain to work create a new bucket with a naked domain, and add the redirect to sub-domain. It doesn't need any bucket policies, or an IAM user. After that add the CNAME DNS record to the bucket endpoint.

If your domain provider does not allow to add CNAME records, or disables them on some use cases (like name.com did, making me lose days of email) try to move to a provider that supports that. If you can't move to a different provider you can use wwwizer.com service which redirects from naked domain to one with www. (using 301 redirect, preserving everything). Just create an A DNS record pointing to an IP address 174.129.25.170.

The upload could be faster, but I'm guessing this might be due to removing deleted objects. It looks like it re-does all the categories XML files (might be worth to check in the future if this could be avoided). The uploads take 20-90 seconds at the moment, so it's fine (for now).

External services like Google Analytics, or SumoMe will not work until you set up the DNS.

Conclusion

I'm happy with the results. Would have been better if I spent less time on this, or some things were easier to do, but I like the end result. It was a fun side project, and I'm finally able to start writing blog posts again!

Posted in: PHP, Technical, Tips

Comments