前言

Reveal.js 是一个美观实用 HTML 演示文稿框架,只需要你决定内容,就可以方便地产出外观优雅的演示文稿。你可以在线查看 Reveal.js 的 Demo

为了分享已经制作好的演示文稿,可以使用 GitHub Pages 进行部署,同时也能够不用安装依赖地开启演讲者视图。以下是我建立 slides.zhanghai.me 的过程。

基本

为了建立一个 GitHub Pages 站点,我们需要准备以下一些文件:

  • 404.html:GitHub Pages 将使用此页面作为默认 404 页面的替代,一般可以换成一个符合自己站点风格的页面。
  • CNAME:对于绑定自定义域名的 GitHub Pages,可以使用这个文件指定自己的自定义域名。
  • .nojekyll:我们的站点不需要 Jekyll 的特性干预,因此将它关闭来避免可能的问题。

然后在 GitHub 上建立仓库,执行以下的命令:

1
2
3
4
5
6
7
git init
# 将默认的分支名 master 改为 gh-pages
git symbolic-ref HEAD refs/heads/gh-pages
git commit -am 'Initial commit.'
# 将 your/repo/origin 替换为你的仓库地址
git remote add origin your/repo/origin
git push --set-upstream origin gh-pages

当然,使用自定义域名的话还要配置一下 DNS 记录。

演示文稿

为了部署 Reveal.js 的演示文稿,可以利用 Git 的 Submodule 来完成对 Reveal.js 项目本身的引用。如此以来,我们的站点项目的 Git 仓库就不用跟踪 Reveal.js 中各个文件的状态,又可以记住所引用的 Reveal.js 仓库当时的版本(Commit SHA1)。

比较幸运的是,GitHub Pages 的构建过程是可以支持 Submodule 的。需要注意的是,Submodule 的仓库地址必须采用 https 而非 ssh 协议(否则会收到构建失败的邮件通知)。以下是将 Reveal.js 作为 Submodule 引入的命令:

1
git submodule add https://github.com/hakimel/reveal.js.git

之后将你的 index.html 中对于 Reveal.js 文件的应用都加上 reveal.js/ 的目录前缀即可。

其他详情可以参考我的站点仓库 DreaminginCodeZH/slides.zhanghai.me

结语

Reveal.js 是一个易用而优雅的 HTML 演示文稿框架,借助于它,我已经多年没有用过 PowerPoint 了。至于利用 GitHub Pages 将它放置在网页端,则可以在免运维的情况下可靠地分享自己的演示文稿,同时也可以不用安装大量 npm 依赖来开启演讲者视图,算是一个简单又有用的小技巧。

因此,将我的配置过程和结果记录在此,希望能对他人有所帮助。

前言

Travis CI 是 GitHub 上开源项目采用持续集成的常见选择。为了给 豆芽 提供持续集成版本用于公开测试,我配置了 Travis CI,并自己编写了脚本将构建结果发布到另一个空项目的 Release 中,并将其间的过程和遇到的问题记录于此。

Travis CI 构建 Android 项目的时间较长,因此调试配置时十分耗时。希望我的经验能对他人有所帮助。

Travis CI

Travis CI 分为免费版(travis-ci.org)和付费版(travis-ci.com),两者之间没有相互的链接或说明,第一次配置时容易混淆。开源项目选择免费版即可。配置过程可以参考官方的 Getting StartedAndroid 项目配置

关于 Android 项目有一些较为微妙的配置问题,我自己调试并查阅了一些 Issue 方才解决。

  1. 为了能够找到并下载最新的 Build Tools,需要启用最新版本的 Tools(- tools)。
  2. Lint 过程中如果 Platform Tools 版本低于 SDK 版本则会报错,需要启用最新版本的 Platform Tools(-platform-tools)。
  3. 为了能够找到并下载最新的 Platform Tools,需要已有最新的 Tools,因此与官方给出的样例不同,需要将 - tools 放置在 - platform-tools 之前。

因此我的 Android 部分最终配置如下:

1
android:
  components:
    - tools
    - platform-tools
    - build-tools-24.0.1
    - android-24
    - extra-android-m2repository

详细配置可以参考我的 .travis.yml

启用构建缓存

Gradle 是一个为缓存优化过的工具,因此官方也提供了相应的 开启缓存的方法

1
before_cache:
  - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:
  directories:
    - $HOME/.gradle/caches/
    - $HOME/.gradle/wrapper/

应用签名

为了给 CI 版本的 APK 签名,需要提供相应的 keystore 和密码。Travis CI 提供了 在设置中定义环境变量 的方式来传递低敏感级的信息。

基于以上文档和一些搜索结果,并参考过 Shadowsocks-Android 的配置方式,我将我的签名配置编写为了从 signing.properties、环境变量、终端输入三个层级进行获取的方式。

以下是我的 signing.gradle

1
2
3
4
5
6
7
8
9
10
11
12
android {
signingConfigs {
release {
Properties signingProperties = new Properties()
signingProperties.load(rootProject.file("signing.properties").newDataInputStream())
storeFile rootProject.file(signingProperties.get("storeFile"))
storePassword signingProperties.get("storePassword") ?: System.getenv("STORE_PASSWORD") ?: System.console()?.readLine("nStore password:")
keyAlias signingProperties.get("keyAlias")
keyPassword signingProperties.get("keyPassword") ?: System.getenv("KEY_PASSWORD") ?: System.console()?.readLine("nKey password:")
}
}
}

在 Android Studio 中进行 Gradle 同步时,System.console() 返回 null,因而密码均为 null,不会中断同步过程,也不影响调试版本的构建。

我创建了 signing.propertiessigning.properties.travis 两个文件,在前者中填写 keystore 的路径并加入 .gitignore,而将后者在 .travis.ymlbefore_script 中复制为 signing.properties

而在 Travis CI 的设置中,添加 STORE_PASSWORDKEY_PASSWORD 两个环境变量即可。建议在环境变量值的两端加上单引号以避免特殊字符被 shell 错误处理。

获取版本信息

直接在 .travis.yml 中调用 git describegit log 等命令是无法成功的,因为 Travis CI 采用了 git clone --depth=50 来进行 clone。相应地,需要先执行 git fetch --unshallow 来完成 clone。

我采用了 语义化版本(Semver)来命名版本。因此,我的版本名称采用了如下方式获取:

1
version="$(git describe --long --tags | sed's/^v//;s/-([0-9]+)-g([0-9a-f]+)/+1.2/')"

例如,在名为 1.0.0-alpha 的 tag 后第 227 次短哈希值为 886f8ce 的 commit,对应的版本名即为 1.0.0-alpha.1+227.886f8ce

然后使用 sed -i 's/versionName .*/versionName "'"${version}"'"/' app/build.gradle 即可更新 build.gradle 中的 versionName 字段。

上传至 Release

GitHub 提供了在 Release 中上传二进制构建输出的功能。然而,如果直接在项目仓库中为每次 commit 添加 Release(和相应的 tag)则未免过于杂乱,因此我选择了新建 一个只有 README 的仓库,并将所有持续集成版本的 Release 创建在此仓库中。

为了实现此功能,我选择了通过 curl 调用 GitHub API 的方式来完成。经过查阅文档和大量的调试,我的脚本最终是如下编写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/bash

set -e

repo="$1"
shift
echo "Repo: ${repo}" >&2

version="$1"
shift
echo "Version: ${version}" >&2

tag="v${version}"
echo "Tag: ${tag}" >&2

body="$1"
shift
echo "Body: ${body}" >&2

# Get old release by tag
echo "Getting old release by tag..." >&2
response="$(curl -H"Authorization: token ${GITHUB_ACCESS_TOKEN}""https://api.github.com/repos/${repo}/releases/tags/${tag}")"
echo "${response}" >&2
old_release_id="$(echo "${response}" | jq -r '.id')"

if [["${old_release_id}" != "null" ]]; then

# Delete old release
echo "Deleting old release..." >&2
response="$(curl -X 'DELETE' -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" "https://api.github.com/repos/${repo}/releases/${old_release_id}")"
echo "${response}" >&2
fi

# Create release
echo "Creating release..." >&2
response="$(curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" -H 'Content-Type: application/json' --data "{ "tag_name": $(echo -n"${tag}"| jq -s -R -r @json), "name": $(echo -n"${tag}"| jq -s -R -r @json), "body": $(echo -n"${body}"| jq -s -R -r @json) }" "https://api.github.com/repos/${repo}/releases")"
echo"${response}">&2
upload_url="$(echo "${response}" | jq -r '.upload_url' | sed 's/{?name,label}$//g')"
echo"Upload url: ${upload_url}">&2

for file in"$@"; do
# Upload file
echo"Uploading file: ${file}">&2
name="$(basename "${file}")"
response="$(curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" -H "Content-Type: $(file -b --mime-type"${file}")" --data-binary "@${file}" "${upload_url}?name=$(echo -n"${name}"| jq -s -R -r @uri)")"
echo"${response}">&2
done

而设置 GITHUB_ACCESS_TOKEN 环境变量后的用法则如:

1
./upload-to-releases.sh 'DreaminginCodeZH/DouyaCiBuilds' "${version}" "${description}" "douya-ci-${version}.apk"

其他

如果无法确定错误原因,就多加些 echo 或者 cat 吧。

如果希望在 Lint 失败时查看输出,可以在 after_failure 中加入 cat app/build/outputs/lint-results-*.html

我所采用的配置都可以在 豆芽 中找到。

结语

为 Android 项目使用 Travis CI 的过程还算简单,但是也有一些微妙的问题需要解决,这令我花费了不少时间。而将每次的构建输出上传至另一个仓库的 Release 则是我考虑了一段时间后得出的方案,之前没见到过这种方式,用 curl 调用 GitHub API 也是第一次,同时再次感受到了 bash 的得心应手,总体上是一次十分有趣的体验。

因此,将我的配置过程和结果记录在此,希望对其他开发者有所帮助。