This is a Jekyll/Octopress project that compiles to the website for GPI, a class at Carnegie Mellon University (15-131).
- Installation
- Recompiling the Site
- Site Layout
- Creating New Content
- Troubleshooting
- Jekyll Tips
- Markdown Tips
- Semantic UI Tips
- Updating
- Deploying
- License
- Appendix
First, clone this repo to your local machine. I've cloned mine to ~/gpi/www/
,
so I may use this as a shorthand for the root of the project from time to time.
This is a Jekyll site. If you don't have an up-to-date version of Ruby,
go get one. OS X comes with a version of Ruby out of the box, but it's outdated and not
easy to upgrade. If you don't already have a way to manage Ruby versions, I
recommend rbenv. See the Appendix for rbenv
installation instructions.
Now that you have Ruby, you can go ahead and install the projects dependencies.
We're going to be using Bundler to manage Ruby gems. For a detailed list of what
will be installed, see the Gemfile
.
As it turns out though, managing module dependencies is a pain. Luckily, the Python community went and figured it out, building a project called virtualenvwrapper. Most of the time it's used for managing Python modules, but when combined with RubyGems and Bundler, it possesses all the features necessary to work similarly for Ruby. If you don't have virtualenvwrapper installed already, go install it. Note that you need python 3.6 or lower!
Once you've got that done, you can go ahead and run
$ make install
which will set up the virtual environment, install the dependencies, and make the magic happen. If you are working on windows, and the script keeps saying you need to install rbenv or virtualenvwrapper even though you already have those installed, accompanied by "$'\r': command not found" error, then consider this solution.
If workon is not recognized as a command, then you probably forgot to edit the
bashrc file as specified in the virtualenvwrapper installation instructions.
My ~/.bashrc
has the following:
export WORKON_HOME=~/.virtualenvs
VIRTUALENVWRAPPER_PYTHON='/usr/bin/python'
source /home/path/to/virtualenvwrapper.sh
where the path to python
and virtualenvwrapper.sh
can be found by doing which python
and which virtualenvwrapper.sh
respectively.
To work in the newly created virtual environment, run
$ workon <name of virtualenv>
To stop working on the virtual environment,
$ deactivate
We're using Octopress 3.0, the most recent version of Octopress, to help with managing our Jekyll site. Octopress 3.0 was rewritten to put the focus back on Jekyll, instead of being a blogging framework in its own right. Jekyll is a blogging framework which makes writing your site's content as easy as putting it in the right place, and Octopress 3.0 helps to put things in the right place.
We're using the version of Sass that comes with Jekyll to build the Sass stylesheets. Sass is a language that compiles to CSS, but adds in tons of cool features that make it easy to write maintainable stylesheets.
To continuously build and preview the site locally:
$ make serve
Content on this site can be divided into three types of pages:
- navigation pages
- These pages help to organize the site. They sit at the top level, and have links in the navigation bar on every page.
- topic pages
- These pages serve as "landing pages" for any given topic. Some examples of topics are Terminal Usage, Vim, and Bash.
- lesson pages
- Each lesson page discusses a single or a small set of things. Every lesson has a parent topic page. Lesson pages should be short and concise. It is favorable to have many short lesson pages than one huge one.
In addition to the navigation bar mentioned above, there is also a sidebar on every page that shows a hierarchy of all the topic and lesson pages.
The home page is the schedule page, which also has a short overview. A longer discussion of the content of the course is left to the Syllabus page.
Notably, the one page that should be a lesson page but that is a navigation page is the "Initial Setup" page. This page is left as a navigation page for a number of reasons:
- Navigation pages are more visible, making them more accessible for first-time viewers.
- Navigation pages serve as points where the user can dive deeper into more complex topics. This encourages the user to read more content. (It's easier to go downhill than uphill.)
Think carefully before making a navigation page. It's likely that you want a new topic and then a new lesson page to host that content.
The different types of pages used on this site are distinguished by their YAML front matter. For more information on YAML (an encoding system similar to JSON), see this page.
To create topic/lesson pages easily with the correct front matter attributes
populated correctly, there are two make
targets which will guide you through
the process of adding a new post. They're just wrappers around Octopress, which
has the octopress new
command to make a new page given some criteria.
# to make a new topic:
$ make topic
# to make a new lesson:
$ make lesson
Each script will print the location of the file it just created, which you can edit to suit your needs.
You can see what's going on under the hood by using
vim -p {_templates,_support}/{topic,lesson}
Basically, we're using octopress new
but specifying a different template using
--template
.
Given what happened with the organizational and content structure of the last site, this site comes with a set of guidelines to be followed when creating new content.
The following words are officially banished from titles by decree of Jacob Zimmerman, Chief BossyPants:
- intro
- introduction
- basic
- intermediate
- advanced
- more
- moar
Additionally, try at all costs to avoid the following words in lessons
- usage
- tips
There will be exceptions to this, but a descriptive name is always better.
Additionally, titles for lessons of the form
- Getting Started with X
are officially reserved for the circumstance when a topic requires special configuration that was not covered in the Initial Setup document. These documents should be short, focus on the bare minimum required to configure the terminal environment, and then link to the next resource that should be viewed.
The site is already a great example of what should and should not be a topic or lesson. Before adding any new content, you should carefully study the existing content to determine what your course of action should be.
There is definitely a time and place for topic pages. That being said, if you can fit a lesson in under an existing topic, it gains the advantage of being grouped with similar content. That way, people will feel free to move around from lesson to lesson, enriching your content.
Don't be afraid to create a topic page, but don't clutter the site with too many.
When it's appropriate to create a lesson, create a lesson. It's easier to cover two small lessons than to arbitrarily split up a large lesson.
As knowledgeable as you are about the topic or lesson you're about to write, you're probably not the first one to write about it. You should carefully research what other people have written about the subject to figure out what each has done well and what needs improvement.
Ideally, what you end up writing should be a perfect synthesis of what each article does well, thus fixing what each article needed individually to suit your needs for writing.
While you're doing your research, you'll probably come across some great resources. Link to them! Putting links in the context of the page is a great way to enrich the content of your own posts. On top of in-line links, all topics pages have a Resources section for accumulating a list of stellar links about that topic.
PDFs and other similar documents should be added under the appropriate topic
that they belong to. For example, Adam Blank's How to LaTeX PDF is placed in
the same directory as the index.md page for LaTeX: topics/latex/
.
For PDFs or documents that don't have a single topic, only put them in one place.
For PDFs or documents that have no topic, perhaps you should create a topic page for that particular topic. To figure out whether you should do this, see "Creating New Content" in this document.
Currently, there is no place to put PDFs or documents that have no topic. If it becomes necessary to place these documents somewhere, bring up the subject at a course staff meeting, outline your reasoning, and make an argument for where you think these documents should be placed.
Keep in mind that there should only be one h1
header (#
in Markdown) per
page (i.e., the title). Your own section headers should use ##
, and your
subsections should use ###
.
Use the {{ "/link-url/" | prepend: site.baseurl }}
Liquid syntax
to create links relative to the site's base URL. This facilitates making
separate sites for each semester (/~15131/f14/...) if we decide to take this
path.
Try to keep paragraph text to 80-character lines. Note that GitHub flavored
Markdown treats inner-paragraph line breaks as <br>
tags, but the Markdown
renderer we're using to build our site doesn't have this feature enabled.
To sort everything, each page has an order
property. Before rendering any
content, all the pages are sorted in order of this property. Because we only
ever render similar content (i.e., topics, lessons, navigation items) together,
only pages that occur "near" each other need to have non-ambiguous order
values. For example, All five navigation
items can have orders from 1 through
5, and all lessons under the terminal-usgage
topic can have orders from 1
through 5, and everything will be sorted appropriately, even if the intermediate
sorted list of all pages intermingles navigation pages with terminal-usage
lesson pages.
If you're not working on the virtual environment (workon <env>
), chances are
that everything will fail with non-descriptive error messages, so check that
you're working on the environment. You should be able to see that you're on an
environment because its name will have been added to your prompt, but otherwise
you can check that the value of $VIRTUAL_ENV
is non-empty.
This is a unordered list of Jekyll tips that I've found useful.
- add
published: false
and changeorder
to0
to the front matter of any page to make it not show up in the generated site. - Checkout the Vim plugin vim-liquid by Tim Pope.
- use
{% highlight <lang> %}... content ...{% endhighlight %}
to have your code snippets highlighted according to a specific language.
You can use Tables in your markdown like this:
| Heading 1 | Heading 2 |
| --------- | --------- |
| A | B |
| C | D |
{:.ui.striped.table}
output:
Heading 1 | Heading 2 |
---|---|
A | B |
C | D |
{:.ui.striped.table} |
The {:.ui.striped.table}
is used by the Markdown rendering engine we're using
(kramdown) to apply CSS classes to the previous HTML block. The CSS framework
we're using (Semantic UI) needs these classes to be present to make the table
look good.
Markdown tables cannot have line breaks in them, so make them as long as they
need to be to hold their content. If you need a line break within a cell, use
<br>
. For an easy way to format Markdown tables, you may want to look at
tabular or vim-markdown. Note that HTML is valid
Markdown, so you can always just add the HTML for a table if you need to
(though, this defeats the simplicity of Markdown, so don't do this often!).
For simple, non-highlighted code blocks, use ~~~
instead of three back ticks.
This is an unfortunate limitation of the Markdown processor that we're using.
That being said, you probably want to use the {% highlight <lang> %}
syntax
mentioned in the Jekyll Tips section.
The CSS framework we're using, Semantic UI, comes with a variety of CSS helper classes. This is a list of common classes you might want to include when writing your pages.
The Markdown processor we're using, kramdown, supports an extension where CSS classes can be applied by appending text of the following form to the end of block:
{:.class1.class2}
To have messages stand out more than bold or italic text, you can include a
.ui.message
. Example:
__WARNING!__ This is my warning message.
{:.ui.warning.message}
There are classes for warning
, error
, info
, success
, and a variety of
other colors. See here for a full list.
If you're using rbenv
, Ruby can be updated by installing a newer version of
Ruby using ruby-build
and then running rbenv local <new version>
.
To update the dependencies, modify the Gemfile
as necessary and use bundle update
to update to the most up to date versions of the gems that we're using
that satisfy the requirements specified in the Gemfile
.
We depend on two static assets: jQuery and Semantic UI.
jQuery is being loaded through the Google CDN. To update it, simply change the
version variable (jquery_version
) in the _config.yml
file.
Semantic UI is being loaded through cdnjs. To update it, simply change the
version variable (semantic_version
) in the _config.yml
file.
We're hosting the site on the www.cs.cmu.edu domain. To get this to work,
there's a Git remote hook that watches for incoming pushes to the develop
branch set up in our AFS space. It
- watches for incoming pushes
- creates a checkout of the code into the
www
directory on our AFS space - installs Jekyll dependencies if necessary
- runs Jekyll to build the site
Git remote hooks don't get version controlled with a repository's content, but nonetheless you can see a copy of this Git hook here (it should hopefully be up to date with whatever's actually deployed, but if you're troubleshooting, make sure you check the copy on AFS).
From here, SCS Facilities takes care of serving our site whenever it's visited.
After having cloned this repo, you can add the AFS remote with
git remote add andrew ssh://[email protected]/afs/cs/academic/class/07131-f18/repos/www.git/
And you can deploy to it by pushing develop
to that remote:
git push andrew develop
After you are done making changes locally, do
jekyll build
To build a rendered version of the website, complete with an index.html
page and proper
links to other pages. By default, the rendered site is stored in the _site/
folder.
scp -r _site/ andrew:~private/
or your prefered command for scp-ing, to your prefered location. Then you can ssh in, and
perform a cp
to transfer the _site/
folder to /afs/cs/academic/class/07131-f18/www
.
Be sure to rename it to f21
afterwards.
While it's admittedly jank, this alternative is good to use if you don't have the direct
permission to edit /afs/
folders, or if you have version issues with Ruby.
Our site's content is always versioned by semester, so people can always access the content they remember, even after their semester has ended. The way this works is:
- Within the
www
folder on AFS, there are folders for the corresponding semesters. - When you push, the Git hook looks at the appropriate semester and builds the
site into the right directory.
- A copy of this script is in
_support
- A copy of this script is in
- When Jekyll builds the site, it knows what semester it is from the
_config.yml
file. - When users visit https://www.cs.cmu.edu/~07131/, there is a single
index.html
file sitting in07131-f18/www/
that refreshes to the current semester.
To bump the semester, you have to manually alter the semester in the last three places just discussed. To recap, you'll have to edit the semester in
SEMESTER
in/afs/cs/academic/class/07131-f18/repos/www.git/hooks/post-receive
and in_support/post-receive
- the refresh URL of
/afs/cs/academic/class/07131-f18/www/index.html
baseurl
in_config.yml
That's a lot of places, but it's only once a semester. If you want to make it better, make it better.
The GitHub pages site (i.e., the master
branch) is just a single HTML file
that redirects to the www.cs.cmu.edu version of the site. This means you only
ever have to push the site to andrew
and the two can never get out of sync.
MIT License. See LICENSE.
Installing rbenv
is easiest on OS X, because you can use Homebrew. If you
don't have OS X, you'll have to install from "source", but almost all of rbenv
is written in shell, so it's not too bad.
For more information, you should absolutely check out the documentation on the rbenv GitHub page.
Install using Homebrew:
$ brew update
$ brew install rbenv ruby-build
Now add these lines to your .bash_profile
/.bashrc
/.zshrc
, etc.:
# Store Ruby versions and information alongside Homebrew instead of in .rbenv
$ export RBENV_ROOT="/usr/local/var/rbenv"
$ eval "$(rbenv init -)"
Now that you've installed rbenv
, you will probably want to see the Appendix
section on building a Ruby version.
Install using Git:
$ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
Now add these lines to your .bash_profile
/.bashrc
/.zshrc
, etc.:
$ export PATH="$HOME/.rbenv/bin:$PATH"
$ eval "$(rbenv init -)"
Now that you've installed rbenv
, you will probably want to see the Appendix
section on building a Ruby version.
To build a Ruby version, find the version you want to build using
$ ruby-build --defintions
Pick the one you want, and then run
$ rbenv install <version>
Certain projects (like this one) have a local Ruby version specified using the
.ruby-version
file. When this is the case, you only need to run
$ rbenv install
and it will install the version specified by that file. If you want to create a
.ruby-version
file for future projects, once you've installed a ruby version,
you can run rbenv local
(or rbenv global
to make it the default across your
whole machine).
The Table of Contents in this file was generated using DocToc.
$ npm install -g doctoc
$ doctoc README.md
From time to time, RubyGems can be a pain. What I'd really like is a
virtualenv-like environment for Ruby gems, but for now what I do when I have
upgrading troubles is uninstall everything according to this
script, and then do a bundle install
to install from the
Gemfile.lock (the last working state).