Update
I have discovered after writing this article that Circle CI has better built-in support for Haskell than Travis CI does. In particular Circle will cache cabal build artifacts for you automatically between builds. I’d recommend using Circle for all your Haskell projects.You can configure things to let you write blog posts directly on Github’s interface and use Travis CI to deploy your changes. Most day-to-day blogging will not require using Haskell at all or even having the Haskell environment installed on the blogger’s machine.
I’ll show you how to set everything up, including an optimized Travis config that can deploy changes in less than a minute. There is some existing information online about doing this sort of thing, but it’s all outdated in one way or another.
We’ll be using Github Pages to serve the final assets. I’ll assume you’re making a static site for a Github organization called
myorg
and want it to live at myorg.io
.Installation
- Create a Github organization. E.g.
myorg
- Create a project
myorg/myorg.github.io
- The master branch will be repeatedly overwritten and committed later
on by Travis, so you won’t make any edits there directly. For now add a
file to the root of the master branch called
CNAME
containing the stringmyorg.io
- Create two
A
records in the DNS formyorg.io
pointing at 192.30.252.153 and 192.30.252.154 respectively. - Generate the base Hakyll project.
# in an empty directory of your choice # NOT in the git repo you've been using cabal sandbox init cabal install -j --disable-documentation hakyll cabal exec hakyll-init myorg.github.io
- Create an orphan source branch in your git repo and copy the generated files there.
git checkout --orphan source git rm CNAME cp -r /path/to/generated/myorg/* . git add .
- Reuse the cabal sandbox you created earlier:
cp -r /where/you/ran/cabal/install/.cabal-sandbox .
- Keep build artifacts out of git by adding these lines to
.gitignore
.cabal-sandbox cabal.sandbox.config dist/ _cache _site
- Run your new site locally to see that it works!
cabal sandbox init cabal run rebuild cabal run watch # now load http://localhost:8000
- Create
.travis.yml
and add the following boilerplate:
language: haskell ghc: 7.8 branches: only: - source before_install: - git submodule foreach --recursive 'git checkout master; git ls-files | grep -v README | grep -v CNAME | xargs -r git rm' install: - curl http://bin.begriffs.com/hakyll/cabal-sandbox.tar.xz | tar xJ - cabal sandbox init - cabal configure --disable-library-profiling --disable-tests --disable-library-coverage --disable-benchmarks --disable-split-objs before_script: - git config --global user.email "$GIT_EMAIL" - git config --global user.name "$GIT_NAME" script: cabal run -j build after_script: - cd _site - export REMOTE=$(git config remote.origin.url | sed 's/.*:\/\///') - git remote add github https://${GH_TOKEN}@${REMOTE} - git add --all - git status - git commit -m "Built by Travis ( build $TRAVIS_BUILD_NUMBER )" - git push github master:master | grep -v http
- Generate a Github auth token.
- Set encrypted environment variables to allow Travis to commit to the master branch
gem install travis travis encrypt -r myorg/myorg.github.io --add GH_NAME="J. Doe" GH_EMAIL=jdoe@myorg.io GH_TOKEN=xxxxxxxx
- Commit all the files.
- Enable Travis for your repo. Instructions here.
- Push the
source
branch to Github. - Watch the deploy progress at https://travis-ci.org/myorg/myorg.github.io
(optional) Generating a custom cabal sandbox for Travis
You can use my shared cabal sandbox on Travis as done above, or you can build your own. It’s a little trickier. Use this Travis config as a start. It takes advantage of post-build deployment to S3.language: haskell
ghc: 7.8
branches:
only:
- source
before_install:
- travis_retry sudo add-apt-repository -y ppa:hvr/ghc
- travis_retry sudo apt-get update
- travis_retry sudo apt-get install --force-yes happy-1.19.3 alex-3.1.3
- export PATH=/opt/alex/3.1.3/bin:/opt/happy/1.19.3/bin:$PATH
install:
- cabal sandbox init
- cabal install -j --disable-documentation -fhttps pandoc
- cabal install -j --disable-documentation --disable-tests --reorder-goals
deploy:
provider: s3
access_key_id: AKIAIYJJY5B5UWSU3CFQ
bucket: YOUR_BUCKET
region: us-west-1
skip_cleanup: true
local-dir: ".cabal-sandbox"
upload-dir: hakyll
acl: !ruby/string:HighLine::String public_read
on:
repo: myorg/myorg.github.io
branch: source
secret_access_key:
secure: YOUR_SECURE_KEY
from
https://begriffs.com/posts/2014-08-12-create-static-site-with-hakyll-github.html
-----------------------
使用 Travis 自动化部署 Hexo Blog
0x00 背景
使用 Hexo + Github Pages 搭建博客后,每次更新文章需要使用
hexo d -g
会在本地生成 public
静态博客网站和向 Github 推送的 .deploy_git
文件夹。.deploy_git
文件夹内容和 public
文件夹一致,但多了 Github 博客项目的仓库信息与提交信息。最终,.deploy_git
文件夹内的全部内容被推送到 Github 仓库中,由 Github Pages 服务完成静态网站的解析。
当切换工作环境后,需要重新安装 Nodejs 以及配置 Hexo 和它的依赖。而且每次更新文章后,都要
hexo d -g
手动部署。这样多次重复的工作非常低效,因此结合现在非常流行的 CI/CD 概念和工具,可以为 Hexo + Github Pages 博客集成 Travis CI 自动部署的能力。当推送博客仓库到 Github 后,由 Travis 自动获取当前 commit 并进行构建,把生成的静态网站推送到 Github Pages 分支。0x01 理解 Hexo 的自动化部署
下图是 Hexo 手动部署的流程,hexo-blog 可以是本地一个项目,也可以是 Github、Gitlab 等仓库,本地配置好 Hexo 环境后,由 ① 触发部署,将本地生成的静态博客网站
.delpoy_git
推送到 Github 的静态博客仓库中。
当引入 Travis 后,整个流程变成了下图所示的流程。hexo-blog 项目必须是一个 Github 仓库了,当有文章更新,本地由 ① 触发,把 Hexo-blog 的源码推送到 Github,剩下的工作由 Travis 完成:获取 Github hexo-blog 仓库中最新的 commit,运行我们定义的
.travis.yml
并把生成的静态博客网站 .deploy_git
推送 Github 静态博客仓库 xxx.github.io。注意,这里要区分 Github 中的两个仓库:静态 blog repo 和 Hexo blog repo。前者是博客网站的静态网站项目,由 Github Pages 托管和解析;后者是 Hexo 项目,前者的内容是由后者生成的。
0x02 如何配置自动化部署
再看一次引入 Travis 后的流程图,绿色箭头的流程是 Travis 自动运行的。要想实现自动化部署,需要 Travis:
- 能够自动获取 hexo-blog 仓库的代码提交;
- 能够从 hexo-blog 仓库的源码中生成静态博客网站 .deploy_git 目录;
- 能够把静态博客网站目录文件推送到静态博客仓库 xxx.github.io 中;
接下来,我们用三个步骤,分别解决上面提到的三个问题。
1. 配置 Travis 获取 Hexo blog
这样,每当 hexo-blog 有新的 commit 时,Travis 都能收到 Gitub 的回调,并从 hexo-blog 仓库中拉取最新提交的代码。
2. 配置 Travis 自动编译
Travis 的自动编译是由项目根目录的
.travis.yml
来控制的。我们需要为 hexo-blog 项目编写一个 .travis.yml
文件,下面是一个 .travis.yml
示例。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | language: node_js node_js: - '10' branches: only: - master before_install: - git config --global user.name "YOUR NAME" - git config --global user.email "YOUR EMAIL" - sed -i'' "s~https://github.com/ |
其中:
language
和node_js
指定了使用 NodeJS docker 进行构建,NodeJS 版本为 10.x;branches
指定只有 master 分支的提交才进行构建;before_install
用于配置推送静态博客网站项目的 git 信息,最重要的是sed -i
这条命令,该命令把 hexo-blog 配置文件_config.yml
中的 git remote URL 替换为携带认证ACCESS TOKEN
的 URL。 这样避免了 Travis 中需要输入用户名密码的问题。关于${ACCESS_TOKEN}
变量会在接下来进行说明;1 2 3 4 5 6 7
# _config.yml ... deploy: type: git repo: https://github.com/
NAME>/ REPO>.git branch: master ...install
指定了每次构建之前需要先安装 hexo 和 hexo-blog 项目中的依赖;script
指定了运行的 npm deploy 命令去执行hexo d -g
。deploy 命令配置在了 hexo-blog 项目的 package.json 中:1 2 3 4 5 6 7
{ ... "scripts": { "deploy": "hexo clean && hexo d -g" }, ... }
3. 配置 Travis 推送博客仓库的权限
Travis 需要把生成的静态博客网站推送到 Github 博客网站仓库,需要有仓库的读写权限,有两个办法为 Travis 配置权限:
- 一个是把 Github SSH Key 放到 Hexo-blog 项目中,Travis 构建完成推送时,使用 SSH Key 推送;
- 另一种办法是在 Github 中为 Travis 生成一个拥有读写仓库权限的 Personal access tokens。上面的 .travis.yml 中使用到的
${ACCESS_TOKEN}
变量便是第二种方法。
关于第一种使用 Github SSH Key 的方法,因为 Hexo-blog 是公开项目,所以直接把 SSH Key 放到项目里是不安全的,因此需要使用 Travis 先把 SSH Key 加密,并在 .travis.yml 中配置解密。该方法配置稍微复杂一点,这里介绍更简单的 Personal access tokens 方法。
-
生成的 token 一定要复制保存下来。
- 在 https://travis-ci.org/account/repositories 中点击 hexo-blog 项目的 settings,添加环境变量,把上面生成的 token 设置为变量,这里的变量名字设置为
ACCESS_TOKEN
,和 .travis.yml 中的配置一致。注意,为了安全不要勾选DISPLAY VALUE IN BUILD LOG
。
至此,自动化部署已经配置好了。
在 Hexo blog 项目中新建一个 git commit,可以在 Travis 中查看项目构建的过程了,不管成功还是失败,你的邮箱都会收到一封构建邮件。
0x03 后记
不论是本文的 Travis 配置,还是其他新鲜的知识,只有理解了技术的原理,才能在遇到问题的时候知道怎么解决,看到别人的解决方法时知道他的思路和为什么要这么做,是否还有更优的方法。