网站更新记录

本网站的进化过程
A banner image: lines of code
A banner image: lines of code

2017年6月

[内容] 添加 卖鸡蛋仔的阿七

2017年4月

[部署] 回到 Hakyll。使用 Amazon CloudFront 进行内容分发。使用 Amazon 提供的 HTTPS 证书。使用 Circle CI 编译和部署。

circle.yml 内容见链接。需要注意的几点:

  • pip 安装 s3cmd,系统 apt 自带的版本太老,且 --default-mime-type 的实现有问题
  • s3cmd 使用时要加 --no-mime-magic 参数,否则它的依赖 python-magic 会影响对文件类型的判断,例如将 CSS 判为 text/plain

[内容] 添加 AI, VR and space explorationGetting started with … StatsDotfile Management and Documentation with Org-Mode

2016年1月

[内容] 添加 育儿史小考

[部署] 从 S3 搬到 DigitalOcean,使用 Docker 进行自动编译和部署。

2015年11月

[部署] 改用 Circle CI 进行自动编译和部署,并将代码打成 cabal 包。

[内容] 添加 GnuPG 使用简介

2015年10月

[外观] 改用新的样式表。增加在页边显示脚注的功能。

[部署] 恢复使用 Hakyll 作为网站引擎。继续使用 Amazon S3 作为文件存储。

2015年9月

[内容] 添加 不死者与生者

2015年4月

[内容] 添加 与不理想的世界和解

[内容] 修订 read-me

[部署] 改用 ikiwiki 并把 host 搬到 DigitalOcean。

2015年1月

[内容] 添加新文章 But I didn’t

[内容] 恢复原来的 Changelog

2014年11月

因为偶然的机会接触到 Asciidoc 这种标记语言,被其强大的描述能力吸引,又因为 Awestruct 对 Asciidoc 支持不错,所以决定将网站生成引擎改成 Awestruct。

[部署]  rbenv 是用于管理 Ruby 版本和环境的软件。它允许不同的项目在不同的 Ruby 版本下运行,每个项目可以有自己的 Gem 配置。

安装方法:

$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ type rbenv
rbenv is a function # <- 说明 rbenv 安装成功了
rbenv ()
{
    local command;
    command="$1";
    if [ "$#" -gt 0 ]; then
        shift;
    fi;
    case "$command" in
        rehash | shell)
            eval "`rbenv "sh-$command" "$@"`"
        ;;
        *)
            command rbenv "$command" "$@"
        ;;
    esac
}

rbenv 本身并不自带安装 Ruby 版本的功能。要直接下载、编译 Ruby 并安装到 rbenv 管理的位置,可以使用同一作者提供的 ruby-build 脚本。

$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

因为本目录下已经有 .ruby-versions 文件,直接运行 rbenv install 命令即可启动 Ruby 的下载和安装。

$ gem install bundler

在我使用的某台 Gentoo 机器上,执行上述命令报错:

/home/snakehsu/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:awestruct-in `require': cannot load such file -- auto_gem (LoadError)
        from /home/snakehsu/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'

在网上简单搜索一下就能得到 答案,这可能是由于 Gentoo 的 dev-lang/ruby 安装过程中会设置 RUBYOPT=-rauto_gem( https://logstash.jira.com/browse/LOGSTASH-2221 ),因此需要取消这个设置:

unset RUBYOPT;   sudo env-update

安装 Asciidoctor 和 awestruct:

$ rbenv rehash
$ bundle install

初始化 awestruct,使用 Foundation 框架:

$ awestruct -i -f foundation
$ rake setup # 确保环境变量正确设置

修改 .gitignore

$ cat >> .gitignore << LINES
/.awestruct/
/.ruby-*
/.sass-cache/
/_site/
/_tmp/
LINES

将其他文件加入 git 管理:

$ git add .

配置 _config/site.yml

添加 movefiles.rb,使两个目录下生成的文件直接到站点根目录下。

修改 _ext/pipeline.rb,调用 movefiles.rb 和优化 URL 的扩展。

修改 _layouts/base.html.haml,加入 metadata 的展示。

安装 Asciidoctor Style Factory:

$ git submodule add -b embedded-stylesheets \
https://github.com/asciidoctor/asciidoctor-stylesheet-factory.git \
stylesheets/_themes

修改 stylesheet/app.scss 以使用新的主题。

使用 Foundation 5 框架:

这个没有 Awestruct 的官方支持,因此我修改了 Gemfile,安装了 Foundation 的命令行工具,并用它生成一个 _foundation 目录,里面有 Foundation 5 框架用到的 SCSS 和 JS 文件。

根据 Awesturct 的 源代码,Compass 的各种选项可以在 _config/compass.rb 中配置。

修改 stylesheet/app.scss 中的 @import 语句,指向 Foundation 5 的 SCSS 组件路径。

[功能] Highlight.js 支持下的语法高亮:

克隆 Highlight.js 代码库并编译:

$ git clone git://github.com/isagalaev/highlight.js.git
$ cd highlight.js
$ node tools/build.js -t browser :common

又是 Gentoo 的坑。用 Portage 里自带的 nodejs ebuild 安装 node 和 npm 的话,不会设置 NODE_PATH 环境变量,因此要先用 npm 安装编译的依赖:

$ sudo npm install -g lodash commander gear del gear-lib

然后带上环境变量编译

$ NODE_PATH=/usr/lib/node_modules:$NODE_PATH node tools/build.js -t browser :common

在 Highlight.js 代码树中 src/styles 里挑一个合适的样式,拷到网站的 stylesheets 目录下并在模板中加入引用语句。最后将编译生成的 build/highlight.pack.js 拷贝到网站代码库中,并修改模板使其能在浏览时加载。

[功能] 参考文献生成

Asciidoctor 支持 &lt;&#8203;&lt;key>> 格式的引用footnote:[注意,前面的两个小于号之间有一个0宽度空白(U+200B),这是我为了阻止 Asciidoctor 把这个例子也识别成一个真正的 cite key 而添加的,直接复制粘贴会有问题。],参考文献信息则以 [bibliography] 块中的列表呈现。我添加了一个 extension,用来从一个 BibTeX 文件中根据正文中的 citation key 生成参考文献(使用 citeproc-ruby 和 bibtex-ruby),并改写正文中的 [bibliography] 区块。

require 'citeproc'
require 'csl/styles'
require 'bibtex'

class CitationProcess

  def execute(site)
    site.pages.each do |page|
      bib = site.bib
      style = site.csl
      cp = CiteProc::Processor.new style: style, format: 'text'
      cp.import BibTeX.open(bib).to_citeproc
      # get list of cited items
      cited = page.raw_content.scan(/<<[\w_:]*>>/).collect {|w| w.gsub!(/[<>]*/, '')}.uniq

        biblio = Array.new
        unless cited.empty?
          cited.each do |key|
            ref = cp.render( :bibliography, id: key )
            page.raw_content.gsub!(Regexp.new("<<#{key}>>", true), "[#cite-#{key}]*<<#{key}>>*")
            entry = ". [[#{key}]]link:#cite-#{key}[[#{key}]] " + ref.join()
            biblio << entry
          end
          page.raw_content.gsub!(/\[bibliography\]/, "[bibliography]" + "\n" + "#{biblio.join("\n")}")
        end
      end
  end
end

2014年8月–11月

由于 Hakyll 遇到 Cabal 的依赖限制问题,这段时间不能在 Travis CI 上自动部署了,于是开始考虑换平台。先后试用了 Middleman,nanoc,Metalsmith,Gulp.js 等等等等,浪费了不少时间。

2014年6月

[部署]增添利用 Travis CI 的自动部署功能。

最近我的生活和工作方式都发生了一些变化:晚上要带孩子,而且工作中使用 iPad 替代笔记本电脑,不能经常使用 MacBook Pro 上的 git repo 来更新网站了。因此我尝试着用 Travis CI 来自动安装依赖、编译 HTML,并上传到 Amazon S3。

  1. Amazon IAM 设置:因为这个配置要把 S3 的访问密钥传到 Travis 的环境中,所以最好是使用 Amazon IAM 生成专门的用户和组并分配权限,这样即使用户的密钥泄露也不会对其他 AWS 的服务产生影响。这个用户或组的访问策略可以这样写:
 {
  "Statement": [
    {
      "Action": [
        "s3:ListAllMyBuckets"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Action": [ 
          "s3:ListBucket", 
          "s3:PutObject",
          "s3:PutObjectAcl",
          "S3:DeleteObject"
      ],
      "Effect": "Allow",
      "Resource": [
          "arn:aws:s3:::expoundite.net", 
          "arn:aws:s3:::expoundite.net/*"
      ]
    }
  ]
}

其中 s3:PutObjectAcl 在网上介绍使用 S3 备份的教程中很少提到,我因为需要给文件加上 public-read 的 ACL 属性,所以需要这句。

  1. .travis.yml:这个文件是 Travis CI 工作时需要读取的各种设置和测试步骤的描述文件。我的设置如下:
language: haskell
env:
  global:
  - secure: DDLQi5WYnCAUhKQNBwq4rBnmYATP/OskewKyEwyw6DymjfBKdH+/enf8rdb8C+eSSJbsTgb3mtDYmgWK7fPTBG6rWLqKayC6CgFTrcG0tEF67KaSLpdk3dtezmlwIcR9jRrI2YqyAmMexoS4MW6zDj4Lv2wqAPGJepdTtbvAOHg=
  - secure: McmMItOzZxayXsb0Yl/8/cSwU13Dc2BLEwOCldN9yXKUXgL2wOki1Xv9/tWLiWFcJoEnffYXcFyY3H04UcKPeI+y1KJsbXwf0YF6Uz2SsqkwtcEi4eKUHnRNGOb3biFzhwWzwI+8iibZx6yAREUyggJncIC9q/DGg/FMoXklc50=
branches:
  only:
    - master
install:
- cabal install hakyll
- sudo apt-get install s3cmd
script:
- ghc --make ./site.hs
- echo "access_key = $AWS_ACCESS_KEY_ID" >> .s3cfg
- echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> .s3cfg
- "./retouch.sh"
- "./site build"
- "./travisupload.sh"

其中,secure: 后面是使用 Travis 命令行工具加密的 AWS Access Key ID 和 Secret Access Key。获得这两行的方法是

 $ travis encrypt AWS_ACCESS_KEY_ID="MYKEYID" --add
 $ travis encrypt AWS_SECRET_ACCESS_KEY="MYSECRETKEY" --add

要注意的是运行前先要确定 .travis.yml 里没有之前生成的 secure 字段;用双引号把 Amazon 那里拷来的 Key ID 和 Key 包起来是可以的,但一定不要把环境变量的名称也用引号包起来,否则解密后会找不到这两个变量。在测试的步骤中,echo 的两行用来把这两个环境变量的值放进 .s3cfg。

  1. .s3cfg:这是 s3cmd 的设置文件。在本机上先用 s3cmd --configure 生成默认的配置,这个过程中 s3cmd 会询问 Key ID 和 Key,填第1步中那个专用用户的即可。测试可以访问 Amazon S3 上的 bucket 后,去掉 access_key = ...secret_key = ... 这两行,再把它放进 repo 里。

  2. 修复文件的修改时间。Travis CI 是使用 Git 的 checkout 命令获取需要测试的代码快照的,这个过程得到的代码文件修改时间都是测试发生当时,这样我的文章的修改时间这个属性就没法用了。在 retouch.sh 这个文件中,我用文件在代码库中最近一次 commit 的时间作为修改时间,并用 touch 命令把文件的修改时间改回去。这一步必须在 Hakyll 开始编译站点前完成。

再加上同步到 S3 的命令,将这些改动 commit 到 master 分支,并在 Travis 里打开与这个代码库的 hook 之后,就可以等 Travis 完成安装编译和上传的全过程了。这样,当 Github 上的 master 分支一有改变,约10分钟后,更新的网站就会自动传到 S3。

2014年5月

[内容]添加关于页面。

2014年4月

[外观]在页脚处增加 Ripple 捐助链接。

[部署]修正联系 email。

[部署]使用七牛云存储作为图床。

[内容]增加关于多因素决策的文章。

[功能]参考 jtanguy@Github 的实现,增加自动从 bib 文件生成参考文献功能。

[内容]增加关于电影化游戏的文章。

2014年3月

[内容]添加了关于理财的文章。

2014年2月

[部署]考虑了成本、可用性等因素后,我决定将网站的内容存储在 Amazon S3 上。和大多数虚拟主机和网页托管服务相比1,S3 在网站内容总量小于1G,访问量小于2万 PV 和流量不超过10G时的性价比是无人能敌的,而且也不用担心被攻击和系统日常维护的问题。

因为我采用了生成的 HTML 不带扩展名的方案,所以在上传内容到 S3 的时候出了一些麻烦。S3 收到文件后,会利用扩展名来猜测文件的 MIME type。对于不带扩展名的文件,S3 自动当成 binary/octet-stream 类型处理,这样就无法访问 HTML 文件中的内容了。

既然生成 HTML 不带扩展名这个做法是从 Gwern 那里学来的,我就在网上搜索他是如何解决这个问题的。很幸运地,他确实在一篇讨论贴中提及了他的解决方法。和 Gwern 的方法不同的是,s3cmd 从1.0.1版本开始,已经支持用 --mime-type 参数设置猜不到 MIME type 时的默认 MIME type。所以上传文件时的命令行这样写就可以:

s3cmd sync _site/ s3://haoyangwrit.es
  --guess-mime-type
  --mime-type 'text/html'
  --recursive
  --delete-removed
  --acl-public

此外,在 name.com 久觅域名无果后,我在 domain.com 买了 haoyangwrit.es 这个域名,还比较满意。现已用亚马逊官方提供的方法将域名绑定到了存放网站的 bucket。

由于在 domain.com 注册域名比较麻烦(需要回复 ICANN 发出的确认邮件,而我似乎还没有看到这个东西),我还是回到了熟悉的 name.com,并注册了 expoundite.net 这个域名,S3 上的 bucket 也相应地改变了。name.com 改变 NS 记录的速度非常快,几分钟以后,我已经用上了用 Amazon Route 53 管理的新域名。

[内容]开始了新文件,关于北京的城市更新。我对这个题目还没有太多概念,但我相信只要进行足够多的思考和阅读,我是可以写出有用的东西来的。

[部署]为了节省 S3 的请求和流量费用,我注册了 CloudFlare 的免费 CDN 服务,而这又要求我继续改变 NS 记录。我已经把 Route 53 上的记录删掉,可以每月节省0.5美元,但因为默认的 TTL 是48小时,此刻我还是在通过原有的 DNS 记录解析域名,也就暂时没有用到 CloudFlare 的功能。

[外观]改善了 figure caption 的显示,现在读者能意识到 figure caption 不是正文的一部分了。

[内容]添加关于个人财务决定的讨论文章

[部署]开始使用 CloudFlare CDN,并利用 CF 的功能添加了 Google Analytics。

[内容]添加两篇旧文章:城市更新背后的逻辑作为工作的研究和作为发现的工作

[功能]使用国产的多说作为评论系统。

2014年1月

[功能]我从 gwern.net 和 Danny Su 的 blog 代码中各抄了一点东西,实现了网站的 RSS 和 tags 页面功能。有一点原创性的工作,就是这个网站虽然采用了和 gwern.net 类似的根据页面更新时间而不是创建时间排列 RSS 项目的功能,但我没有像他那样用 Gitit 里的 module 来实现,而是在 fannesposito.com 的代码基础上重写了 createdFirst 这个函数,利用 System.Directory module 中的 getModificationTime 函数来获取文件的更新时间:

createdFirst :: [Item String] -> Compiler [Item String]
createdFirst items =
    let itemsWithTime = unsafeCompiler $ forM items $ \item -> do
        utc <- getModificationTime $ toFilePath $ itemIdentifier item
        return (utc,item);
    in liftM (\xs -> reverse . map snd $ sortBy (comparing fst) xs) itemsWithTime

然后用类似调用 Hakyll 自带的 recentFirst 函数的方式调用 createdFirst 获得前10项最新的更新。

[外观]我仿照 gwern.net 的样式写了个很丑但还算能看的 CSS,并利用 Typeplate 和 normalize.css,搭起了网站基本的样子。

用符号字体改善了导航栏。字体用的是来自 fontsquirrel.com 的 Entypo,由 Daniel Bruce 设计,按照 Creative Commons BY-SA v3.0 许可证发布。


  1. 甚至包括以价格低廉著称的 nearlyfreespeech.net 和 Hostigation。