GitHub implements Subresource Integrity

With Subresource Integrity (SRI), using GitHub is safer than ever. SRI tells your browser to double check that our Content Delivery Network (CDN) is sending the right JavaScript and CSS to your browser. Without SRI, an attacker who is able to compromise our CDN could send malicious JavaScript to your browser. To get the benefits of SRI, make sure you're using a modern browser like Google Chrome.

New browser security features like SRI are making the web a safer place. They don't do much good if websites don't implement them though. We're playing our role, and encourage you to consider doing the same.

You can read more about Subresource Integrity and why we implemented it on the GitHub Engineering blog.

GitHub Desktop is now available

The new GitHub Desktop is now available. It's a fast, easy way to contribute to projects from OS X and Windows. Whether you're new to GitHub or a seasoned user, GitHub Desktop is designed to simplify essential steps in your GitHub workflow and replace GitHub for Mac and Windows with a unified experience across both platforms.

desktop

Branch off

Branches are essential to proposing changes and reviewing code on GitHub, and they’re always available in GitHub Desktop’s repository view. Just select the current branch to switch branches or create a new one.

Collaborate

Craft the perfect commit by selecting the files—or even the specific lines—that make up a change directly from a diff. You can commit your changes or open a pull request without leaving GitHub Desktop or using the command line.

Merge and Deploy

Browse commits on local and remote branches to quickly and clearly see what changes still need to be merged. You can also merge your code to the master branch for deployment right from the app.

GitHub-flow

Ready to start collaborating? Download GitHub Desktop. If you're using GitHub for Mac or Windows, the upgrade is automatic.

Adopting the Open Code of Conduct

We are proud to be working with the TODO Group on the Open Code of Conduct, an easy-to-reuse code of conduct for open source communities. We have adopted the Open Code of Conduct for the open source projects that we maintain, including Atom, Electron, Git LFS, and many others. The Open Code of Conduct does not apply to all activity on GitHub, and it does not alter GitHub's Terms of Service.

Open source software is used by everyone, and we believe open source communities should be a welcoming place for all participants. By adopting and upholding a Code of Conduct, communities can communicate their values, establish a baseline for how people are expected to treat each other, and outline a process for dealing with unwelcome behavior when it arises.

The Open Code of Conduct is inspired by the code of conducts and diversity statements from several other communities, including Django, Python, Ubuntu, Contributor Covenant, and Geek Feminism. These communities are leaders in making open source welcoming to everyone.

If your project doesn't already have a code of conduct, then we encourage you to check out the Open Code of Conduct and consider if your community can commit to upholding it. Read more about it on the TODO Group blog.

GitHub Extension for Visual Studio is open source

Last April we released the GitHub Extension for Visual Studio, which lets you work on GitHub repositories in Visual Studio 2015. To celebrate Microsoft's final release of Visual Studio 2015, we're making the GitHub Extension for Visual Studio open source under the MIT License.

We'd like to thank Microsoft for their help and support in the development of the GitHub extension. In addition, this project wouldn't have been possible without open source tools, libraries, and assorted projects that are publicly available. We look forward to contributing back to the community and helping other developers leverage our work to create their own extensions for Visual Studio.

Download the GitHub Extension for Visual Studio now to see it in action. To file an issue or contribute to the project, head on over to the repository. We look forward to your pull requests!

Read-only deploy keys

You can now create deploy keys with read-only access. A deploy key is an SSH key that is stored on your server and grants access to a single GitHub repository. They are often used to clone repositories during deploys or continuous integration runs. Deploys sometimes involve merging branches and pushing code, so deploy keys have always allowed both read and write access. Because write access is undesirable in many cases, you now have the ability to create deploy keys with read-only access.

viewing and adding deploy keys

New deploy keys created through GitHub.com will be read-only by default and can be given write access by selecting "Allow write access" during creation. Access level can be specified when creating deploy keys from the API as well.

The GitHub Engineering Blog

We are happy to introduce GitHub's Engineering Blog to the world. Starting today, you can read details about our infrastructure, learn about our development practices, and hear about the knowledge we've gained while running the world's largest code collaboration platform.

You can also get updates by following our Engineering Twitter account @GitHubEng.

Happy reading!

Eight lessons learned hacking on GitHub Pages for six months

Believe it or not, just over a year ago, GitHub Pages, the documentation hosting service that powers nearly three-quarters of a million sites, was little more than a 100-line shell script. Today, it's a fully independent, feature-rich OAuth application that effortlessly handles well over a quarter million requests per minute. We wanted to take a look back at what we learned from leveling up the service over a six month period.

What's GitHub Pages

GitHub Pages is GitHub's static-site hosting service. It’s used by government agencies like the White House to publish policy, by big companies like Microsoft, IBM, and Netflix to showcase their open source efforts, and by popular projects like Bootstrap, D3, and Leaflet to host their software documentation. Whenever you push to a specially named branch of your repository, the content is run through the Jekyll static site generator, and served via its own domain.

Eating our own ice cream

At GitHub, we're a big fan of eating our own ice cream (some call it dogfooding). Many of us have our own, personal sites hosted on GitHub Pages, and many GitHub-maintained projects like Hubot and Electron, along with sites like help.github.com, take advantage of the service as well. This means that when the product slips below our own heightened expectations, we're the first to notice.

We like to say that there's a Venn diagram of things that each of us are passionate about, and things that are important to GitHub. Whenever there's significant overlap, it's win-win, and GitHubbers are encouraged to find time to pursue their passions. The recent improvements to GitHub Pages, a six-month sprint by a handful of Hubbers, was one such project. Here's a quick look back at eight lessons we learned:

Lesson one: Test, test, and then test again

Before touching a single line of code, the first thing we did was create integration tests to mimic and validate the functionality experienced by users. This included things you might expect, like making sure a user's site built without throwing an error, but also specific features like supporting different flavors of Markdown rendering or syntax highlighting.

This meant that as we made radical changes to the code base, like replacing the shell script with a fully-fledged Ruby app, we could move quickly with confidence that everyday users wouldn't notice the change. And as we added new features, we continued to do the same thing, relying heavily on unit and integration tests, backed by real-world examples (fixtures) to validate each iteration. Like the rest of GitHub, nothing got deployed unless all tests were green.

Lesson two: Use public APIs, and when they don't exist, build them

One of our goals was to push the Pages infrastructure outside the GitHub firewall, such that it could function like any third-party service. Today, if you view your OAuth application settings you'll notice an entry for GitHub Pages. Internally, we use the same public-facing Git clone endpoints to grab your site's content that you use to push it, and the same public-facing repository API endpoints to grab repository metadata that you might use to build locally.

For us, that meant adding a few public APIs, like the inbound Pages API and outbound PageBuildEvent webhook. There's a few reasons why we chose to use exclusively public APIs and to deny ourselves access to "the secret sauce". For one, security and simplicity. Hitting public facing endpoints with untrusted user content meant all page build requests were routed through existing permission mechanisms. When you trigger a page build, we build the site as you, not as GitHub. Second, if we want to encourage a strong ecosystem of tools and services, we need to ensure the integration points are sufficient to do just that, and there's no better way to do that than to put your code where your mouth is.

Lesson three: Let the user make the breaking change

Developing a service is vastly different than developing an open source project. When you're developing a software project, you have the luxury of semantic versioning and can implement radical, breaking changes without regret, as users can upgrade to the next major version at their convenience (and thus ensure their own implementation doesn't break before doing so). With services, that's not the case. If we implement a change that's not backwards compatible, hundreds of thousands of sites will fail to build on their next push.

We made several breaking changes. For one, the Jekyll 2.x upgrade switched the default Markdown engine, meaning if users didn't specify a preference, we chose one for them, and that choice had to change. In order to minimize this burden, we decided it was best for the user, not GitHub, to make the breaking change. After all, there's nothing more frustrating than somebody else "messing with your stuff".

For months leading up to the Jekyll 2.x upgrade users who didn't specify a Markdown processor would get an email on each push, letting them know that Maruku was going the way of the dodo, and that they should upgrade to Kramdown, the new default, at their convenience. There were some pain points, to be sure, but it's preferable to set an hour aside to perform the switch and verify the output locally, rather than pushing a minor change, only to find your entire site won't publish and hours of frustration as you try to diagnose the issue.

Lesson four: In every communication, provide an out

We made a big push to improve the way we communicated with GitHub Pages users. First, we began pushing descriptive error messages when users' builds failed, rather than an unhelpful "page build failed" error, which would require the user to either build the site locally or email GitHub support for additional context. Each error message let you know exactly what happened, and exactly what you needed to do to fix it. Most importantly, each error included a link to a help article specific to the error you received.

Errors were a big step, but still weren't a great experience. We wanted to prevent errors before they occurred. We created the GitHub Pages Health Check and silently ran automated checks for common DNS misconfigurations on each build. If your site's DNS wasn't optimally configured, such as being pointed to a deprecated IP address, we'd let you know before it became a problem.

Finally, we wanted to level up our documentation to prevent the misconfiguration in the first place. In addition to overhauling all our GitHub Pages help documentation, we reimagined pages.github.com as a tutorial quick-start, lowering the barrier for getting started with GitHub Pages from hours to minutes, and published a list of dependencies, and what version was being used in production.

This meant that every time you got a communication from us, be it an error, a warning, or just a question, you'd immediately know what to do next.

Lesson five: Optimize for your ideal use case, not the most common

While GitHub Pages is used for all sorts of crazy things, the service is all about creating beautiful user, organization, and project pages to showcase your open source efforts on GitHub. Lots of users were doing just that, but ironically, it used to be really difficult to do so. For example, to list your open source projects on an organization site, you'd have to make dozens of client-side API calls, and hope your visitor didn't hit the API limit, or leave the site while they waited for it to load.

We exposed repository and organization metadata to the page build process, not because it was the most commonly used feature, but because it was at the core of the product's use case. We wanted to make it easier to do the right thing — to create great software, and to tell the world about it. And we've seen a steady increase in open source marketing and showcase sites as a result.

Lesson six: Successful efforts are cross-team efforts

If we did our job right, you didn't notice a thing, but the GitHub Pages backend has been completely replaced. Whereas before, each build would occur in the same environment as part of a worker queue, today, each build occurs in its own Docker-backed sandbox. This ensured greater consistency (and security) between builds.

Getting there required a cross-team effort between the GitHub Pages, Importer, and Security teams to create Hoosegow, a Ruby Gem for executing untrusted Ruby code in a disposable Docker sandbox. No one team could have created it alone, nor would the solution have been as robust without the vastly different use cases, but both products and the end user experience are better as a result.

Lesson seven: Match user expectations, then exceed them

Expectations are a powerful force. Everywhere on GitHub you can expect @mentions and emoji to "just work". For historical reasons, that wasn't the case with GitHub Pages, and we got many confused support requests as a result. Rather than embark on an education campaign or otherwise go against user expectations, we implemented emoji and @mention support within Jekyll, ensuring an expectation-consistent experience regardless of what part of GitHub you were on.

The only thing better than meeting expectations is exceeding them. Traditionally, users expected about a ten to fifteen minute lag between the time a change was pushed and when that change would be published. Through our improvements, we were able to significantly speed up page builds internally, and by sending a purge request to our third-party CDN on each build, users could see changes reflected in under ten seconds in most cases.

Lesson eight: It makes business sense to support open source

Jekyll may have been originally created to power GitHub Pages, but since then, it has become its own independent open source project with its own priorities. GitHubbers have always been part of the Jekyll community, but if you look at the most recent activity, you'll notice a sharp uptick in contributions, and many new contributors from GitHub.

If you use open source, whether it's the core of your product or a component that you didn't have to write yourself, it's in your best interest to play an active role in supporting the open source community, ensuring the project has the resources it needs, and shaping its future. We've started "open source Fridays" here at GitHub, where the entire company takes a break from the day-to-day to give back to the open source community that makes GitHub possible. Today, despite their beginnings, GitHub Pages needs Jekyll, not the other way around.

The numbers

Throughout all these improvements, the number of GitHub Pages sites has grown exponential, with just shy of a three-quarters of a million user, organization, and project sites being hosted by GitHub Pages today.

GitHub Pages sites over time

But the number of sites tells only half the story. Day-to-day use of GitHub Pages has also seen similar exponential growth over the past three years, with about 20,000 successful site builds completing each day as users continuously push updates to their site's content.

GitHub Pages builds per day

Last, you'll notice that when we introduced page build warnings in mid-2014, to proactively warn users about potential misconfigurations, users took the opportunity to improve their sites, with the percentage of failed builds (and number of builds generating warnings) decreasing as we enter 2015.

GitHub Pages is a small but powerful service tied to every repository on GitHub. Deceivingly simple, I encourage you to create your first GitHub Pages site today, or if you're already a GitHub Pages expert, tune in this Saturday to level up your GitHub Pages game.

Happy publishing!

Large Scale DDoS Attack on github.com

We are currently experiencing the largest DDoS (distributed denial of service) attack in github.com's history. The attack began around 2AM UTC on Thursday, March 26, and involves a wide combination of attack vectors. These include every vector we've seen in previous attacks as well as some sophisticated new techniques that use the web browsers of unsuspecting, uninvolved people to flood github.com with high levels of traffic. Based on reports we've received, we believe the intent of this attack is to convince us to remove a specific class of content.

We are completely focused on mitigating this attack. Our top priority is making sure github.com is available to all our users while deflecting malicious traffic. Please watch our status site or follow @githubstatus on Twitter for real-time updates.

OctoTales • Epic Games

We recently caught up with some of our friends at Epic Games in Cary, NC a few weeks ago to talk about the latest developments with Unreal Engine and Unreal Tournament. Since opening up the engine source code on GitHub a year ago, they've released seven major updates that incorporate an array of contributions from the community such as @SRombauts' git plugin and much improved Linux support.

Our latest OctoTale captured some of Epic Games' stories and some of the amazing work that's going on in their passionate vibrant community. We think you'll enjoy it.

And finally, some more exciting news for fans of the Unreal Engine: as of this week, developers can enjoy free access to Unreal Engine 4!

The Game Off Returns!

GitHub Game Off III

The GitHub Game Off, our very own game jam is returning next week! We've had some great games submitted in previous years and can't wait to see what you come up with this year.

We'll announce the theme and the details on the blog on March 13th at 9am PDT, so please stay tuned. We may even have a twist in store!

The official Twitter hashtag for the Game Off is #ggo15.

Git 2.3 has been released

The Git developers have just released a major new version of the Git command-line utility, Git 2.3.0.

As usual, this release contains many improvements, performance enhancements, and bug fixes. Full details about what's included can be found in the Git 2.3.0 release notes, but here's a look at what we consider to be the coolest new features in this release.

Push to deploy

One way to deploy a Git-based web project is to keep a checked-out working copy on your server. When a new version is ready, you log into the server and run git pull to fetch and deploy the new changes. While this technique has some disadvantages (see below), it is very easy to set up and use, especially if your project consists mostly of static content.

With Git 2.3, this technique has become even more convenient. Now you can push changes directly to the repository on your server. Provided no local modifications have been made on the server, any changes to the server's current branch will be checked out automatically. Instant deploy!

To use this feature, you have to first enable it in the Git repository on your server by running

$ git config receive.denyCurrentBranch updateInstead

When shouldn't you use push-to-deploy?

Deploying by pushing to a Git repository is quick and convenient, but it is not for everybody. For example:

  • Your server will contain a .git directory containing the entire history of your project. You probably want to make extra sure that it cannot be served to users!
  • During deploys, it will be possible for users momentarily to encounter the site in an inconsistent state, with some files at the old version and others at the new version, or even half-written files. If this is a problem for your project, push-to-deploy is probably not for you.
  • If your project needs a "build" step, then you will have to set that up explicitly, perhaps via githooks.

See how this feature was implemented

Faster cloning by borrowing objects from existing clones

Cloning a remote repository can involve transferring a lot of data over the network. But if you already have another local clone of the same repository, it probably already has most of the history that the new clone will need. Now it is easy to use those local objects rather than transferring them again:

$ git clone --reference ../oldclone --dissociate https://github.com/gitster/git.git

The new --dissociate option tells Git to copy any objects it can from local repository ../oldclone, retrieving the remainder from the remote repository. Afterwards, the two clones remain independent; either one can be deleted without impacting the other (unlike when --reference is used without --dissociate).

See how this feature was implemented

More conservative default behavior for git push

If you run git push without arguments, Git now uses the more conservative simple behavior as the default. This means that Git refuses to push anything unless you have defined an "upstream" branch for your current branch and the upstream branch has the same name as your current branch. For example:

$ git config branch.autosetupmerge true
$ git checkout -b experimental origin/master
Branch experimental set up to track remote branch master from origin.
Switched to a new branch 'experimental'
$ git commit -a -m 'Experimental changes'
[experimental 43ca356] Experimental changes
$ git push
fatal: The upstream branch of your current branch does not match
the name of your current branch.  To push to the upstream branch
on the remote, use

    git push origin HEAD:master

To push to the branch of the same name on the remote, use

    git push origin experimental

$

The new default behavior is meant to help users avoid pushing changes to the wrong branch by accident. In the case above, the experimental branch started out tracking master, but the user probably wanted to push the experimental branch to a new remote branch called experimental. So the correct command would be git push origin experimental.

The default behavior can be changed by configuring push.default. If you want to go back to the version 1.x behavior, set it to matching:

$ git config --global push.default matching

See how this feature was implemented

More flexible ssh invocation

Git knows how to connect to a remote host via the SSH protocol, but sometimes you need to tweak exactly how it makes the connection. If so, you can now use a new shell variable, GIT_SSH_COMMAND, to specify the command (including arguments) or even an arbitrary snippet of Shell code that Git should use to connect to the remote host. For example, if you need to use a different SSH identity file when connecting to a Git server, you could enter

$ GIT_SSH_COMMAND='ssh -i git_id' git clone host:repo.git

See how this feature was implemented

The credential subsystem is now friendlier to scripting

When Git needs a password (e.g., to connect to a remote repository over http), it uses the credential subsystem to query any helpers (like the OS X Keychain helper), and then finally prompts the user on the terminal. When Git is run from an automated process like a cron job, there is usually no terminal available and Git will skip the prompt. However, if there is a terminal available, Git may hang forever, waiting for the user to type something. Scripts which do not expect user input can now set GIT_TERMINAL_PROMPT=0 in the environment to avoid this behavior.

See how this feature was implemented

Other

Some other useful tidbits:

  • Now Git is cleverer about not rewriting paths in the working tree unnecessarily when checking out particular commits. This will help reduce the amount of redundant work done during software builds and reduce the time that incomplete files are present on the filesystem (especially helpful if you are using push-to-deploy). See how this feature was implemented
  • Now git branch -d supports a --force/-f option, which can be used to delete a branch even if it hasn't been merged yet. Similarly, git branch -m supports --force/-f, which allows a branch to be renamed even if the new name is already in use. This change makes these commands more consistent with the many other Git commands that support --force/-f. See how these features were implemented

Additional resources

Don't forget: an important Git security vulnerability was fixed last December. If you haven't upgraded your Git client since then, we recommend that you do so as soon as possible. The new release, 2.3.0, includes the security fix, as do the maintenance releases 1.8.5.6, 1.9.5, 2.0.5, and 2.1.4, which were released in December.

Keeping GitHub OAuth Tokens Safe

While making your source code available in a public GitHub repository is awesome, it's important to be sure you don't accidentally commit your passwords, secrets, or anything else that other people shouldn't know.

Starting today you can commit more confidently, knowing that we will email you if you push one of your OAuth Access Tokens to any public repository with a git push command. As an extra bonus, we'll also revoke your token so it can't be used to perform any unauthorized actions on your behalf.

For more tips on keeping your account secure, see "Keeping your SSH keys and application access tokens safe" in GitHub Help.

GitHub Security Bug Bounty program turns one

It's already been a year since we launched the GitHub Security Bug Bounty, and, thanks to bug reports from researchers across the globe, 73 previously unknown security vulnerabilities in our applications have been identified and fixed.

Bugs squashed

Of 1,920 submissions in the past year, 869 warranted further review, helping us to identify and fix vulnerabilities fitting nine of the OWASP top 10 vulnerability classifications. 33 unique researchers earned a cumulative $50,100 for the 57 medium to high risk vulnerabilities they reported.

Bounty submissions per week

We also saw some incredibly involved and creative vulnerabilities reported.

Our top submitter, @adob, reported a persistent DOM based cross-site scripting vulnerability, relying on a previously unknown Chrome browser bug that allowed our Content Security Policy to be bypassed.

Our second most prolific submitter, @joernchen, reported a complex vulnerability in the communication between two of our backend services that could allow an attacker to set arbitrary environment variables. He followed that up by finding a way to achieve arbitrary remote command execution by setting the right environment variables.

New year, higher payouts

To kick off our Bug Bounty Program's second year, we're doubling the maximum bounty payout, from $5000 to $10000. If you've found a vulnerability that you'd like to submit to the GitHub security team for review, send us the details, including the steps required to reproduce the bug. You can also follow @GitHubSecurity for ongoing updates about the program.

Thanks to everyone who made the first year of our Bug Bounty a success. Happy hunting in 2015!

How to write the perfect pull request

As a company grows, people and projects change. To continue to nurture the culture we want at GitHub, we've found it useful to remind ourselves what we aim for when we communicate. We recently introduced these guidelines to help us be our best selves when we collaborate on pull requests.

Approach to writing a Pull Request

  • Include the purpose of this Pull Request. For example:
    This is a spike to explore…
    This simplifies the display of…
    This fixes handling of…
  • Consider providing an overview of why the work is taking place (with any relevant links); don’t assume familiarity with the history.
  • Remember that anyone in the company could be reading this Pull Request, so the content and tone may inform people other than those taking part, now or later.
  • Be explicit about what feedback you want, if any: a quick pair of :eyes: on the code, discussion on the technical approach, critique on design, a review of copy.
  • Be explicit about when you want feedback, if the Pull Request is work in progress, say so. A prefix of “[WIP]” in the title is a simple, common pattern to indicate that state.
  • @mention individuals that you specifically want to involve in the discussion, and mention why. (“/cc @jesseplusplus for clarification on this logic”)
  • @mention teams that you want to involve in the discussion, and mention why. (“/cc @github/security, any concerns with this approach?”)

Offering feedback

  • Familiarize yourself with the context of the issue, and reasons why this Pull Request exists.
  • If you disagree strongly, consider giving it a few minutes before responding; think before you react.
  • Ask, don’t tell. (“What do you think about trying…?” rather than “Don’t do…”)
  • Explain your reasons why code should be changed. (Not in line with the style guide? A personal preference?)
  • Offer ways to simplify or improve code.
  • Avoid using derogatory terms, like “stupid”, when referring to the work someone has produced.
  • Be humble. (“I’m not sure, let’s try…”)
  • Avoid hyperbole. (“NEVER do…”)
  • Aim to develop professional skills, group knowledge and product quality, through group critique.
  • Be aware of negative bias with online communication. (If content is neutral, we assume the tone is negative.) Can you use positive language as opposed to neutral?
  • Use emoji to clarify tone. Compare “:sparkles: :sparkles: Looks good :+1: :sparkles: :sparkles:” to “Looks good.”

Responding to feedback

  • Consider leading with an expression of appreciation, especially when feedback has been mixed.
  • Ask for clarification. ("I don’t understand, can you clarify?")
  • Offer clarification, explain the decisions you made to reach a solution in question.
  • Try to respond to every comment.
  • Link to any follow up commits or Pull Requests. (“Good call! Done in 1682851”)
  • If there is growing confusion or debate, ask yourself if the written word is still the best form of communication. Talk (virtually) face-to-face, then mutually consider posting a follow-up to summarize any offline discussion (useful for others who be following along, now or later).

These guidelines were inspired partly by Thoughtbot's code review guide.

Our guidelines suit the way we work, and the culture we want to nurture. We hope you find them useful too.

Happy communicating!

How GitHub uses GitHub to document GitHub

Providing well-written documentation helps people understand, make use of, and contribute back to your project, but it's only half of the documentation equation. The underlying system used to serve documentation can make life easier for the people writing it—whether that's just you or the team you work with.

The hardest part about documentation should be deciding which words to use, not configuring tools or figuring out how to deploy updates. Members of the GitHub Documentation Team come from backgrounds where crude XML-based authoring tools and complicated CMSs are the norm. We didn't want to use those tools here so we've spent a good deal of time configuring our own documentation workflow and set-up.

We've talked before about how we use GitHub to build GitHub; here's a look at how we use GitHub Pages to serve our GitHub Help documentation to millions of readers each month.

Our previous setup

A few months ago, we migrated our Help site from a custom-built Rails app to a static Jekyll site hosted on GitHub Pages. Our previous Help site consisted of two separate repositories:

  • A Rails application, which was responsible for managing the site, the assets, and the search implementation.
  • The actual content, which was just a grouping of Markdown files.

Our Rails app was hosted on a third-party service; as updates were made to the code, we deployed them with Hubot and Chatops, as we do with the rest of GitHub.

Our typical writing workflow looked like this:

  • The Documentation Team took note when a new feature was shipping.
  • We'd create a new issue to track the feature.
  • When we were ready, we'd open a pull request to start iterating on the content.
  • When the content was in a good place, we'd @mention the team (@github/docs) and have a peer editor review our words.
  • When the feature was ready to ship, we'd merge the pull request. A webhook would fire from the content repository to our hosted Rails app; the webhook's payload updated a database row containing the article's raw Markdown.

Here's an example conversation from @neveret and @bernars showing a bit of our normal editing workflow:

Sample conversation

Working with pull requests was fantastic, because it directly matched the GitHub flow we use across the company. And we liked writing in Markdown, because its syntax enabled us to effectively describe new features in no time.

However, our Rails implementation was a fairly complicated setup:

  • Our reliance on an external host required dedicated employees on our Engineering, Ops, and Security teams to monitor the site and respond to incidents as they arose.
  • Our Documentation team couldn't easily view local changes to the content. Even though we wrote in Markdown, we'd still need to set up a local instance of the Rails app and run a script to import the content into a database, just to see how it would look on the site.
  • We were constantly tweaking the Rails server, but noticed that each request a reader made to the site was still slow. The HTML was being generated on-the-fly, requiring calls to the database and constantly iterating on stronger caching strategies.

We knew we could do much better.

Our new setup

When Jekyll 2.0 was released, we saw an opportunity to replace our existing setup with a static site. The new Collections document type lets you define a file structure that matches your needs. In addition, Jekyll 2.0 introduced support for Sass and CoffeeScript assets, which simplifies writing front-end code.

Open source is great because it's, well, open. As we migrated to Jekyll, we made several pull requests to components of Jekyll, making it a better tool for users of GitHub Pages.

Very little of our original workflow has changed. We still write in Markdown and we still open pull requests for an editorial review. When the pull request is merged, the GitHub Pages site is automatically built and deployed within seconds.

Here's a quick rundown on how we're using core Jekyll features and a handful of plugins to implement the help site.

Gems we use

We intentionally rely on core Jekyll code as much as possible, to minimize our reliance on maintaining custom plugins.

Jekyll 2.0 introduced a new plugin type called a Converter that transforms any markup into HTML. This frees the writer up to compose content however she chooses, and Jekyll will just serve the final HTML. For example, you can write your posts in AsciiDoc, if that's your thing.

To that end, we wrote jekyll-html-pipeline, an implementation of our own open-source html-pipeline. This ensures that the content on our Help site looks the same as content everywhere on GitHub. We also wrote our own Markdown filter to provide some syntax extensions that make writing documentation much easier.

Search

With the previous Rails site, we were using an ElasticSearch provider that indexed our database and implemented a search system for our Help site.

Now, we use lunr-js to provide a faster client-side search experience. In sifting through our analytics, we found that the vast majority of our users relied on an external search provider to get to our documentation. It didn't make sense, during or after the migration, to expend much energy on a server-side search solution.

Content references

The Docs team really wanted to use "content references," or conrefs, when writing documentation. A conref allows you to write a chunk of text once and reuse it throughout the site. (The idea was borrowed from the DITA standard.)

The old Rails app wouldn't permit us to write reusable content, but now we can with the power of Jekyll's data files. For example, we've defined a file called conrefs.yml, and have a set of key-value strings that look something like this:

repositories:
  create_new:
    1. In the upper-right corner of any page, click {{ octicon-plus Plus symbol }}, and then click **New repository**.
      ![New repository menu](/assets/images/help/repository/repo-create.png)

Our keys are grouped by specificity (repositories.create_new); the values they contain are just plain Markdown ("In the upper-right corner..."). We can now reuse this single step across several pages of content that refer to creating a new repository by writing the appropriate Liquid syntax:

To start the process:

{{ site.data.conrefs.repositories.create_new }}
2. Do something else.
3. You're done!

As GitHub's UI evolves, we might need to change the image or rewrite the directional pointer. With a conref, we only have to make the change in one location, rather than a dozen.

Versioned documentation

Another goal of the move was to be able to provide versioned Help documentation. With the release of Enterprise 2.0.0, we began to provide different content sets for the previous 11.10.340 and the current 2.0 releases. In order to do that, we build the Jekyll site with a special audience flag, and check in the generated HTML as part of our Pages repository.

For example, in our config.yml file, we set a key called audience to 11.10.340. If a feature exists that's available in Enterprise 2.0 but not 11.10.340, we demarcate the section using Liquid tags like this:

{% if site.audience != '11.10.340' %}

This new feature...

{% endif %}

Again, this is just taking advantage of core features in Jekyll; we didn't need to build or maintain any aspect of this.

Testing our site

Just because the site is static doesn't mean that we should avoid test-driven development writing.

Our first line of defense for testing content has always been html-proofer. This tool helps verify that none of our links and images are broken by quickly validating every URL in our built site.

Rubyists are familiar with using Capybara to simulate website interactions in their tests. Would it be crazy to implement a similar idea with our static site? Nope! Our own @bkeepers wrote a blog post four years ago talking about this very problem. With that, we were able to write stronger tests that covered our content and our site behavior. For example, we check that a referenced conref is valid (by looking up the key in the YAML file) or that our JavaScript is functioning properly.

Our Help documentation runs with CI to ensure that nothing broken ever gets in front of our readers:

Our help-docs CI build

Speed

As mentioned above, our new Pages implementation is significantly faster than the old Rails app. This is partly because the site is a bunch of static HTML files—nothing is fetched from a database. More significantly, we've already spent a lot of time configuring our Pages servers to be blazing fast for everyone. The same advantages we have, like serving assets off of a CDN, are also available to every GitHub user.

Help docs GA site load times

Making GitHub Pages work for you

Documentation teams across GitHub can take advantage of the GitHub Flow, Jekyll 2.0, and GitHub Pages to produce high-quality documentation. The benefits that GitHub Pages provides to our Documentation team is already available to any user running a GitHub Pages site.

With our move to Pages, we didn't rebuild any new components. We spent far less time building anything and more time discussing a workflow that made sense for our team and company. By committing to using the same hosting features we provide to every GitHub user, we were able to provide better documentation, faster. Our internal workflow has made us more productive, and enabled us to provide features we never could before, such as versioned content.

If you have any questions on our setup, past or present, we're happy to help!