• Stars
    star
    240
  • Rank 168,229 (Top 4 %)
  • Language
    Shell
  • Created almost 10 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An example of automatically updating GitHub Pages when you're using Travis CI.

Automatically Update Github Pages with Travis

Build Status

Do you want to update Github Pages automatically, and use Travis CI? You've come to the right place.

Both versions:

The problem

Here's a few things, which when combined, cause a problem.

  1. Checking in generated files is considered poor practice.
  2. Often, web pages aren't in HTML directly: they're generated from some other file.
  3. master is the default branch of git repositories.
  4. gh-pages is the default branch for Github Pages.

Our source files end up on one branch, but we need to move the generated files to another branch. And of course, we don't want to just do this on every build, but on successful CI builds of master. Whew!

The solution

Follow these steps.

Ensure you have gh-pages

You want to make sure your branch already exists.

$ git checkout master
$ git checkout --orphan gh-pages
$ git push origin -u gh-pages
$ git checkout master

Easy enough.

Find out your Github API token

Click this link to generate a new Personal access token. You might need to re-enter your password.

You'll need to check some boxes. Check these ones:

github token setting

That's right, just repo. If your repository is public, you can set public_repo instead.

GitHub will create the token, and show you a flash with the value.

THIS IS THE ONLY TIME YOU GET TO SEE THIS SO DON'T CLICK AWAY IMMEDIATELY!

You'll need to copy this token into someplace you trust. I wrote mine down, so I could just light the paper on fire afterward. πŸ˜‰. It'll never be shown to you after this time, so it's important to double-check your work.

Set up Travis

There are multiple ways to do this.

Set the variables on the Travis-CI dashboard

  • Go to the settings page of your repository on https://travis-ci.org/
  • In the Environment Variables section set a variable with the name of GH_TOKEN and the value of your personal access token.

Set the variables in the .travis.yml file

With Node.js and Python 3.x installed:

$ npm install travis-encrypt -g
$  travis-encrypt -r username/repository GH_TOKEN=[the token you created before]

With Ruby installed:

$ gem install travis
$   travis encrypt -r username/reponame GH_TOKEN=[the token you created before] --add

Note: that I put some spaces before the travis command. If you have bash configured in this common way, this makes sure the command doesn't end up in your Bash History. Can't be too safe with those tokens.

Note: You'll need to have enabled travis for your repo before this.

Check out this page to read more about variable encryption in Travis.

Edit your .travis.yml

Here's what this should look like:

language: something
install:
  - npm install
script:
  - make check
  - make generate
after_success:
  - bash deploy.sh
env:
  global:
    - secure: "oFD/tic8JAwpMXuMDBZXV4ot6w1NLWvHQnrDKmUHSMQJC1cbbrR1p5q8XayfjtmdqQdFQmIfM6YHEKeHw//ypgObWjYS8q00OaaMDXPTdmgr1Ee4nhgkkDihT+kVij0rn96W/QvyAVoaV5hJoyUr3Nhk+mnHEYm3M+Q3LAQglRg="

Let's go over this, line by line:

language: something

This should be set to whatever the language is of your project. What happens if your project's build tool is different than your project itself? You may need to add an install line to install the other tools. Like for example things listed in package.json.

This should look something like this if you use Node.js:

language: node_js
node_js:
  - "4.1"
install:
  - npm install

Next, our actual build:

script:
  - make check
  - make generate

This changes based on whatever your build actually is. I show this section because you will generally want to run more than one command: one to run the tests one to build your project, and one to build the actual documentation.

after_success:
  - bash deploy.sh

Ok so now we have a successful build so we want to start the deploy. Note that we can have a different approach:

after_success:
  - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && bash deploy.sh

Here we want to check out where we are. We only want to update Github Pages if we're building the master branch of the original repository, so we have to check $TRAVIS_PULL_REQUEST and $TRAVIS_BRANCH.

If we are here, we run bash deploy.sh. What's the contents of deploy.sh? We'll talk about that in a moment. We have one more line to cover:

env:
  global:
    - secure: "oFD/tic8JAwpMXuMDBZXV4ot6w1NLWvHQnrDKmUHSMQJC1cbbrR1p5q8XayfjtmdqQdFQmIfM6YHEKeHw//ypgObWjYS8q00OaaMDXPTdmgr1Ee4nhgkkDihT+kVij0rn96W/QvyAVoaV5hJoyUr3Nhk+mnHEYm3M+Q3LAQglRg="

This, of course, should use the value from travis encrypt from before. Remember how we encrypted GH_TOKEN=... before? This will ensure that our GH_TOKEN variable is set to the unencrypted value. That sounds scary, but Travis will not set this on forks or pull requests, so that someone can't just submit a PR that echoes the value out.

Set up deploy script

Okay, next, we need to add a deploy.sh to our repository. You'll need to tweak this slightly for your setup, but here's the basic idea:

#!/bin/bash

set -o errexit -o nounset

if [ "$TRAVIS_BRANCH" != "master" ]
then
  echo "This commit was made against the $TRAVIS_BRANCH and not the master! No deploy!"
  exit 0
fi

rev=$(git rev-parse --short HEAD)

cd stage/_book

git init
git config user.name "Steve Klabnik"
git config user.email "[email protected]"

git remote add upstream "https://$GH_TOKEN@github.com/rust-lang/rust-by-example.git"
git fetch upstream
git reset upstream/gh-pages

echo "rustbyexample.com" > CNAME

touch .

git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages

Let's do it, paragraph by paragraph:

#!/bin/bash

The standard shebang line. We don't really need to set this, as we execute it with bash deploy.sh, but I like to put it in anyway.

set -o errexit -o nounset

This sets two options for the shell to make the script more reliable:

  • errexit: stop executing if any errors occur, by default bash will just continue past any errors to run the next command
  • nounset: stop executing if an unset variable is encountered, by default bash will use an empty string for the value of such variables.
if [ "$TRAVIS_BRANCH" != "master" ]
then
  echo "This commit was made against the $TRAVIS_BRANCH and not the master! No deploy!"
  exit 0
fi

Here we ensure that we only deploy when we commit against the master branch, if not we just simply abort the deploy, no errors. So this way we can see the result of the tests when we make pull request between different branches or commit against a different branch than the master.

Note: This only works if in .travis.yml we used the unconditional deploy.

after_success:
  - bash deploy.sh
rev=$(git rev-parse --short HEAD)

This sets a variable, rev, with the short hash of HEAD. We'll use this later in a commit message.

cd _book

We need to cd into wherever our website built. With Jekyll, it's _site. But do whatever.

git init
git config user.name "Steve Klabnik"
git config user.email "[email protected]"

First, we initialize a new git repository. Yes, a new one. You'll see.

We then set our user name and user email. This person will have done the commits that go to gh-pages. It's not a default branch, so don't worry, GitHub doesn't count these commits as contributions for your graph.

git remote add upstream "https://$GH_TOKEN@github.com/me/project.git"
git fetch upstream
git reset upstream/gh-pages

Next, we add a remote, named upstream, and we set it to our project. But we also interpolate that $GH_TOKEN variable, which will allow us to push to this repository later.

We then fetch it and reset to the gh-pages branch. Now, git sees this new repository as just some files that change your upstream gh-pages branch.

echo "myproject.com" > CNAME

Sometimes, you'll need some extra files. A CNAME is common, which sets a custom domain up. You'll need to run whatever commands generate those files for you.

touch .

We then touch everything, so that git considers all of our local copies fresh.

git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages

We then add all changes, commit them, using our rev from earlier, and then push to upstream. The -q keeps this a bit more quiet, and you can control the noisiness of all these different git commands with a judicious sprinkling of -q.

Success!

That's it! Commit this all, and push. Travis should now do its magic, and everything will update!

One Drawback

One drawback of this is that if you have a build matrix that builds your project with multiple versions of your platform, you'll end up with the same number of pages builds. Which seems redundant.

I think I could take advantage of Travis' "deploy" feature to fix this, but I'm not sure how.

Feedback please

I'd love to know if there's a better way to do any of this. In particular, I'd love to add the local git repo rather than the one from GitHub when fetching the upstream, but since Travis checks out a bare repository, it doesn't seem possible. Please open an issue or PR to show me how to do it better!

More Repositories

1

request_store

Per-request global storage for Rack.
Ruby
1,415
star
2

CLOSURE

Thanks, _why.
502
star
3

rust_for_rubyists

Learn Rust
HTML
425
star
4

frappuccino

Functional Reactive Programming in Ruby.
Ruby
354
star
5

simple-server

A simple webserver built on top of the Rust standard library and the http crate.
Rust
180
star
6

doxidize

Amazing documentation tooling for Rust
Rust
151
star
7

rustbook

A simplified version of gitbook, atop rustdoc
Rust
124
star
8

mono_logger

A lock-free logger for Ruby 2.0
Ruby
107
star
9

rustdoc

Not a real thing, see https://github.com/rust-lang/rust for rustdoc's actual source code
104
star
10

words

A way to turn markdown into HTML and ebooks
102
star
11

rust_example

A Ruby gem, implemented in Rust
Ruby
101
star
12

indexlist

indexlist: A doubly linked list, backed by a vector
Rust
83
star
13

rust-in-ten-slides

Short presentations about Rust syntax + concepts
HTML
72
star
14

turbolinks_test

Seriously. Use numbers.
Ruby
71
star
15

rustmvc

TodoMVC, with Rust and Ember
JavaScript
70
star
16

mojikun

A programming language that you'll ❀️
Ruby
70
star
17

metadown

Annotate your Markdown files with metadata.
Ruby
69
star
18

ruby-sys

Low-level bindings to Ruby
Rust
66
star
19

jujutsu-tutorial

trying to figure out this jujutsu thing
47
star
20

becoming

Add additional functionality to objects.
Ruby
42
star
21

pomodoro

A pomodoro counter, that turns off your access to certain sites.
Ruby
42
star
22

issue2pr

Transmute your Issues into Pull Requests
CSS
39
star
23

ticketee_review

The example application for Rails 4 in Action.
Ruby
38
star
24

sunlight-congress

A wrapper for the Sunlight Foundation's Congress API
Ruby
36
star
25

history-of-rust

A talk about the history of Rust
HTML
35
star
26

security_release_practice

Ruby
34
star
27

semver-parser

A parser for the semver specification
Rust
33
star
28

blog

This is my blog.
CSS
32
star
29

ticketee

The sample app for Rails 4 in Action
Ruby
32
star
30

dining_philosophers

The Dining Philosophers problem, in Rust
Rust
28
star
31

ref_slice

Take a reference and get back a slice of length one
Rust
26
star
32

hateoas

Build easy clients for Hypermedia APIs.
Ruby
25
star
33

adventure

A text adventure game, in Rust.
Rust
24
star
34

derp

Lets you herp all of your Strings with a to_derp method.
Ruby
21
star
35

trpl

don't worry about it
20
star
36

get_a_job

A hypermedia job queue
Ruby
20
star
37

bring_back_snowman

Ruby
20
star
38

buck-rust-hello

A demo of using buck2 with Rust.
Starlark
19
star
39

json-merge_patch

An implementation of the snell-merge-patch draft for JSON.
Ruby
18
star
40

ticketee_backup

A ticking application. The example app for "Rails 4 in Action"
Ruby
17
star
41

livestream

the source of the stuff I do on twitch.tv
Rust
16
star
42

how_i_start

A sample project for How I Start.
Ruby
16
star
43

no_secrets_anymore

JavaScript
15
star
44

pastebin

This is a copy of the old pastebin 6.0 code
PHP
15
star
45

teachmehowtomakearubygem

Shows you how to make a ruby gem.
Ruby
15
star
46

warehouse

Crates.io's index as a web app.
JavaScript
14
star
47

ludum

My entry for Ludum Dare 35
Rust
13
star
48

forward2015

My talk from forward2015
HTML
13
star
49

abnf

A ABNF parser for Ruby.
Ruby
13
star
50

dtp

Document Transfer Protocol
12
star
51

media_type_template

A template for media type definitions
JavaScript
11
star
52

rwc

A wc clone in Rust
Rust
10
star
53

hypermedia-presentation

JavaScript
10
star
54

require_relative

This backports require_relative to Ruby 1.8.
Ruby
10
star
55

from_behind

Rogues do it from behind; a roguelike in Haskell
Haskell
9
star
56

eployday

A deploy bot for IRC
Ruby
9
star
57

open_company_talk

A talk about open companies.
JavaScript
8
star
58

frp_shoes

Functional Reactive Programming + Shoes
Ruby
8
star
59

grok

Fun with compilers, interpreters and such.
Rust
8
star
60

password-cracker

ceasar cypher cracker in rust
Rust
7
star
61

oredev2018

My talk at Oredev 2018: rustc errors a UX perspective
JavaScript
6
star
62

uniq_ptr_problem

Unsafety with uniq_ptr in C++
HTML
6
star
63

maze_solver

Some experiments with Maze+XML
Ruby
6
star
64

not-the-rust-blog

CSS
5
star
65

meloria

CRM for tattoo shops
Ruby
5
star
66

the_rust_programming_language

Makefile
5
star
67

bad_idea

this is a bad idea
Makefile
5
star
68

parse-example

Parsing a file to a struct in Rust
Rust
5
star
69

antisocialne.ws

fuck reddit
Ruby
5
star
70

heroku-buildpack-rbx

Shell
5
star
71

extraextra

Need to add a news feed to your application? EXTRA! EXTRA! is just the Gem for you!
Ruby
5
star
72

nobody_knows_rust

JavaScript
5
star
73

rustconf2018-wasm-workshop

The WebAssembly workshop at RustConf 2018
HTML
5
star
74

rust-algebra

Monoids, semigroups, and other algebraic structures in Rust
Rust
5
star
75

nasm-rust

Rust
4
star
76

guessing_game

The classic guessing game in Rust.
Rust
4
star
77

rust-adt

A collection of abstract data types, written in Rust
Rust
4
star
78

compass-rust

Rust
4
star
79

cryptopals

http://cryptopals.com/ in Rust
Rust
4
star
80

fosdem2015

My slides for FOSDEM 2015
JavaScript
4
star
81

totally-not-semver

Rust
4
star
82

aoc2017

Advent of Code 2017
HTML
4
star
83

s

My own personal sub: https://github.com/37signals/sub
Shell
4
star
84

rust-linked-lists

Several linked list implementations in Rust
Rust
4
star
85

steveklabnik.com

My personal website.
MDX
4
star
86

geekout2016

The code from my talk at GeekOut
Rust
4
star
87

ActiveRepository

A brain dump of a new persistence strategy for Rails.
Ruby
4
star
88

art_of_ruby

JavaScript
4
star
89

avr-emulator

Atmel 8-bit AVR Emulator in React and Rust
Rust
3
star
90

coinbaser

Coinbase REST API for Rust
Rust
3
star
91

bitcoin-secp256k1-rs

Rust language bindings for Bitcoin secp256k1 library.
Rust
3
star
92

learn_c_the_hard_way_exercises

3
star
93

rust_documentation

A talk about Rust's docs.
JavaScript
3
star
94

heroku-buildpack-rust-wasm

Shell
3
star
95

attendr

Ruby
3
star
96

semver.crates.io

Rust
3
star
97

daw

A little DAW, in Shoes
Ruby
3
star
98

booster2018

Our workshop at BoosterConf 2018
HTML
3
star
99

workers-steveklabnik.com

My personal website
HTML
2
star
100

webscale-ruby-version

This makes RUBY_VERSION web scale
Ruby
2
star