动手

动手

基于Travis CI搭建Android自动打包发布工作流

{%}

作者/ 徐谦

徐谦(AlloVince)有八年Web开发:两年于日本工作,三年于家乡兰州联合创办公司失败,后入职华尔街见闻继续创业。他是开源爱好者,关注PHP及Web开发,曾向多个知名PHP开源项目提交代码。译有《自制编程语言》、《游戏开发的数学和物理》等。技术Blog : http://avnpc.com/

最近付费购买了Travis CI,Travis CI的收费模式很有意思,不是按项目或者用户,而是按工作进程收费,比如初级版本是$129/月,总共提供2个工作进程。在项目不多的情况下,除了用于跑单元测试外,不免想利用的更充分一些,因此抽空搭建了一套基于Travis CI的Android自动发布工作流

未自动化前安卓开发总是避免不了这样的工作流程:

1. 开发一些新功能,提交代码。

2. 完成一部分功能后,打包一个测试版APK。

3. 将测试版APK上传到QQ群 / 网盘 / Fir.im / 蒲公英等。

4. 在QQ群或发布平台解释当前版本所完成的功能。

5. 通知测试人员测试。

实现了这套自动化发布后,工作流程被简化成:

1. 开发新功能,提交代码。

2. 通过git tag对代码打一个内测版的tag,在tag的描述中对写当前完成的功能。

Tag提交后Travis CI会自动编译代码,生成APK文件并分发到Github和fir.im,Github和fir.im中会保持Tag的描述信息,分发完成后会有邮件通知所有参与测试的人员。而作为开发人员,只需要专注于对代码打好一个Tag就可以了。

整个流程看似做了不少工作,其实体现在Travis CI只有数行指令而已,以下逐一讲解:

对安卓项目启用Travis CI

Travis CI应该可以算是目前最好用的持续集成服务之一了,如果代码库是基于Github的话,可以很简单的开启。由于本文涉及到了很多Travis CI的基础概念,建议首先对Travis CI的自定义构建一节有所了解。

很早前在介绍PHP项目的持续集成时也写过如何在PHP项目中使用Travis CI。 对于安卓项目来说步骤几乎一致:

首先准备一个.travis.yml文件放在安卓项目根目录下,.travis.yml中记录了Travis CI所需的基础信息:

language: android

sudo: false

android:
  components:
  - build-tools-23.0.1
  - android-23
  - extra-android-m2repository
  - extra-android-support

script:
  - "./gradlew assembleRelease"

无需读文档就可以通过上面的配置大概知道,我们要运行的是一个安卓项目,安卓SDK版本为23,项目所用的BuildTools版本为23.0.1,为编译这个项目我们还引入了一些必须的组件,如Support Library(extra-android-support)、Android Support Repository(extra-android-m2repository)等。

当Travis CI准备好我们所需要的环境后,将自动运行yml文件script部分所设置的指令,上例中运行的是./gradlew assembleRelease,运行成功的话会在项目的主模块下生成build/outputs/apk/app-release.apk

最后进入Travis CI主页,使用有项目Admin权限的Github帐号直接登录。选择要开启Travis CI的项目,将右边的开关设为On即可。

Travis CI目前有2个网站:如果是开源项目,直接进入travis-ci.org即可,如果是私有付费项目,则需要进入travis-ci.com,2个网站除了域名外所有的界面及操作几乎一模一样。

配置中还有一行sudo: false,是为了开启基于容器的Travis CI任务,让编译效率更高。

安卓自动化构建的密码和证书安全

安卓项目发布需要证书文件和若干密码,但无论是开源项目还是私有项目,任何时候都不应该将原始证书或密码放入代码库(原则上来讲证书和密码也不应该交于开发人员,而应该只能通过发布服务器进行编译)。Travis CI为此提供了2种解决方案,一种是对敏感信息、密码、证书等进行对称加密,在CI构建环境时解密,另一种是将密码等通过Travis CI的控制台(即网站)设置为构建时的环境变量。

由于前者会在Travis控制台生成一对环境变量,所以我的做法是尽量选择后者,但由于Travis控制台无法上传文件,因此涉及到文件加密的部分,则只能选择前者。

说了这么多,首先还是需要先对编译脚本进行改造,如果不考虑安全问题,项目的build.gradle文件可能会是这样:

android {
    signingConfigs {
        releaseConfig {
            storeFile file("../keys/evandroid.jks")
            storePassword "123456"
            keyAlias "evandroid_alias"
            keyPassword "654321"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.releaseConfig
        }
    }
}

而我们最终要的效果,还是希望一份编译脚本既可以用于开发环境,也可以在CI环境下使用,在Travis CI中,可以通过点击项目名称 -> Settings -> Environment Variables中设置环境变量,比如我们可以针对上面的配置,分别设置KEYSTORE_PASSALIAS_NAMEALIAS_PASS三个环境变量,在Travis CI环境下可以通过System.getenv()获得这些环境变量。

本地开发环境中,我的做法是将这几个变量加到gradle.properties文件中,这样就可以在build.gradle内直接使用了。下面是开发环境的gradle.properties

KEYSTORE_PASS=123456
ALIAS_NAME=evandroid_alias
ALIAS_PASS=654321

这样一来build.gradle就变成了:

    releaseConfig {
        storeFile file("../keys/evandroid.jks")
           storePassword project.hasProperty("KEYSTORE_PASS") ? KEYSTORE_PASS : System.getenv("KEYSTORE_PASS")
           keyAlias project.hasProperty("ALIAS_NAME") ? ALIAS_NAME : System.getenv("ALIAS_NAME")
           keyPassword project.hasProperty("ALIAS_PASS") ? ALIAS_PASS : System.getenv("ALIAS_PASS")
    }

接下来处理证书文件,为了方便文件加密等功能,Travis CI提供了一个基于ruby的CLI命令行工具,可以直接使用gem安装。

gem install travis

安装后进入安卓项目根目录,尝试对证书文件加密:

travis encrypt-file keys/evandroid.jks --add

如果首次运行,travis会提示需要登录,运行travis login --org并输入Github用户名密码即可。(付费版则为travis login --pro

travis encrypt-file指令会做几件事情:

1. 在Travis CI控制台自动生成一对密钥,形如:encrypted_e41864bb9dab_key,encrypted_e41864bb9dab_iv

2. 基于密钥通过openssl对文件进行加密,上例中会项目根目录生成evandroid.jks.enc文件。

3. 在.travis.yml中自动生成Travis CI环境下解密文件的配置,上例运行后可以看到.travis.yml中多了几行:

before_install:
- openssl aes-256-cbc -K $encrypted_e41864bb9dab_key -iv $encrypted_e41864bb9dab_i -in keys/evandroid.jks.enc -out keys/evandroid.jks -d

Travis CI默认在项目根目录下运行,因此注意根据实际需求调整enc文件的路径。

最后别忘了在.gitignore中忽略keys/evandroid.jks以及gradle.properties并在代码库中将其删除。

Travis CI自动发布安卓apk文件到Github Release

Travis CI的script部分运行成功后,可以通过配置文件进入到发布阶段。下面是一个Travis CI发布的示例:

deploy:
  provider: releases
  user: "GITHUB USERNAME"
  password: "GITHUB PASSWORD"
  file: app/build/outputs/apk/app-release.apk
  skip_cleanup: true
  on:
    tags: true

这个例子中配置了这样一些内容:

  • provider:发布目标为Github Release,除了Github外,Travis CI还支持发布到AWS、Google App Engine等数十种provider

  • Github用户名和密码,因为Travis CI要上传APK文件,因此需要有Github项目的写入权限。

  • file: 发布文件,输入文件路径即可。

  • skip_cleanup: 默认情况下Travis CI在完成编译后会清除所有生成的文件,因此需要将skip_cleanup设置为true来忽略此操作。

  • on: 发布的时机,这里配置为tags: true,即只在有tag的情况下才发布。

虽然这样就能完成自动发布,但是直接暴露了Github密码是我们更加不能接受的。更好的做法是在Github -> settings -> Personal access tokens 生成一个只能访问当前项目并只有读取权限的Github Access Token,并通过Travis CI将Access Token加密。听起来有点繁琐,好在Travis CLI中已经可以通过一行指令做好这一切:

travis setup release

根据提示填写上述配置项目的信息后,Travis CLI会自动在.travis.yml文件中生成好所有的配置项:

deploy:
  provider: releases
  api_key:
    secure: XXX
  file: app/build/outputs/apk/app-release.apk
  skip_cleanup: true
  on:
    tags: true
    all_branches: true

其中api_key下的secure就是加密后的Access Token。

在运行travis setup release时有可能遇到:

Invalid scheme format: git@github.com for a full error report, run travis report

这样的报错,看起来是Travis CLI还不支持通过密钥访问Github,因此可以将项目的源临时切换为http形式,运行成功后再切换回来:

git remote set-url origin https://github.com/AlloVince/evandroid.git
git remote set-url origin git@github.com:AlloVince/evandroid.git

在实际部署过程中,发现发布到Github Release比较坑的点是:

git push
git push --tags

往往会同时生成2个Travis CI任务,但是在Travis网页中默认界面只能看到最后跑的一个任务,而未打Tag的任务又会报:

Skipping a deployment with the releases provider because this is not a tagged commit

这曾让我一度以为自己的脚本哪里写错了,但是又找不到错误原因……

自动发布APK到fir.im

自动发布到Github对于开发人员已经足够,但是考虑到项目实际需要以及国情,还是有必要选择一个国内的App分发服务,fir.im、蒲公英都是不错的选择,不但允许游客下载,还提供了二维码等更适合对接手机的功能,国内下载速度也很快。由于fir.im提供了比较方便的CLI工具,因此本文以fir.im为例,在.travis.yml中添加以下几行:

before_install:
- gem install fir-cli
after_deploy:
- fir p app/build/outputs/apk/app-release.apk -T $FIR_TOKEN -c "`git cat-file tag $TRAVIS_TAG`"

即在环境构建阶段安装fir-cli,在发布成功后通过fir命令行工具将apk上传到fir。

其中$FIR_TOKEN可以在fir.im的用户->API Token中找到,然后在Travis CI控制台中创建环境变量FIR_TOKEN并粘贴即可。

这里有个小技巧,如果我们仅仅上传APK文件到fir.im,看到链接的测试人员其实并不知道这次发布所包含的变动,因此通过git cat-file tag $TRAVIS_TAG将当前发布tag所包含的附加信息一同上传了。其中$TRAVIS_TAG变量是Travis CI每次运行自动附带的环境变量,还有很多其他的Travis环境变量供我们玩出更多花样。

发布完毕后自动发邮件通知

虽然Travis CI也有通知功能,但不能定制模板,通知内容也仅仅为提示CI运行的结果,显然更适合开发人员。我们还是希望最终能以更友好的方式通知团队成员,同时考虑到邮件送达率,可以优先选择如SubmailSendCloud等国内邮件发送服务。

这里以Submail为例,首先需要在Submail内创建邮件模板,比如我们可以创建这样一封触发式邮件模板:

Hi 亲

@var(TRAVIS_REPO_SLUG)新版本@var(TRAVIS_TAG)已经发布了,功能更新:

@var(TAG_DESCRIPTION)

去下载:
http://fir.im/w13s

创建后可以得到邮件模板id,根据Submail手册,将模板中所需要的变量置入,最终可以使用一行Curl指令发送一封邮件:

after_deploy:- curl -d "appid=10948&to=allo.vince@gmail.com&subject=[自动通知] 安卓新版本$TRAVIS_TAG发布&project=u2c0r2&signature=$SUBMAIL_SIGN&vars={\"TRAVIS_REPO_SLUG\":\"$TRAVIS_REPO_SLUG\",\"TRAVIS_TAG\":\"$TRAVIS_TAG\",\"TAG_DESCRIPTION\":\"$(git cat-file tag $TRAVIS_TAG | awk 1 ORS='<br>')\"}" https://api.submail.cn/mail/xsend.json

其中Submail用到的认证凭据signature同样是通过Travis CI控制台配置的。

总结

最终完成的示例项目在此。其实所有的yml文件配置不到30行,就能省去繁琐的日常工作,何乐而不为呢。最后回顾一下自动化后的日常工作:

提交代码:

git add .
git commit -m "这里是注释"
git push origin

打Tag:

git tag -a v0.0.1-alpha.1 -m "这里是Tag注释,说清楚这个版本的主要改动,也可以省略-m参数直接写长文本"
git push origin --tags

如果发现打错了tag,可以删除本地及远程tag。

git tag -d v0.0.1-alpha.1
git push origin --delete tag v0.0.1-alpha.1

大部分Tag标签虽然仅用于内测,但是仍然建议允许版本语义化原则。

参考

如何发布自己的播客

{%}

作者/ 李喆

李喆,网名Sneezry,吉林大学电子科学与工程学院在读硕士,主要研究方向是计算机视觉的硬件实现。他曾在锤子科技云平台做前端实习生,已拿到微软中国上海分部软件开发工程师Offer。去年,李喆写了《Chrome扩展及应用开发》一书。

相信很多人都有自己的独立博客,拥有自己富有个性的域名,甚至很多人还为自己的博客配置了 SSL 增加安全性防止页面被 ISP 劫持,那么大家想过发布自己的播客吗?其实发布播客并没有想象中的难,我与 RebornixSNK 老师就在这几天发布了我们自己的播客——三人行,下面我就来和大家一起分享一下我们发布播客的整个过程,带领大家一步一步发布自己的播客!

我们来发布自己的播客吧!

提到发布自己的播客,不得不谈一谈我们当初发布播客的初衷。9 月末的一个下午,Rebornix 与我和 SNK 说,我们一起做播客吧!Wow,我们可没有什么播音的经验,这也是大部分人做播客的一个顾虑,不敢将自己的声音发给别人听。不过自媒体播客的优点也在这里,播主无需专业的播音知识,只要有干货就会有听众,即使没有多少听众,单纯做成一件事也是非常有成就感的,所以这点顾虑并没有过多影响我们的计划。Rebornix 的想法也很简单,我们只想做我们自己的播客而已,仅此而已。所以如果你也想做一个自己的播客,那么请记住,播客是为自己做的,不要有太多的负担 :-D

选择一个有趣的域名

我承认,一个网站的价值在内容上,但一个好域名可以为你的网站加不少分,更重要的是,会让你更容易投入进去。我们在做播客前,着实费了好大的力气起名字找域名。如果你的预算充足,fm 域名是个非常好的选择,如果你的资金不够充裕,也可以考虑 io 或 sh 等程序员们喜欢的域名,如果你的目标听众不是程序员,就选择 com 等常规的后缀。

多人 Remote 录制播客

通常来说,播客需要多人一同录制,而多数情况下是需要 Remote 录制的。三人行的第一期中的三个声音就是分别来自北京、上海和深圳。我们使用的是 Skype 多人语音通话,通话质量还是很不错的。录音软件我们使用的是 ocenaudio 这款来自巴西免费的开源软件,值得强调的是,ocenaudio 是一款支持 Windows、Linux 和 OS X 的全平台软件。

后期剪辑

在录音时,没有必要准备太过详细的草稿,甚至是完整的稿件,因为这样会给人很不自然的感觉,我们只要准备一个粗糙的提纲即可,那么这样就不得不面对一个问题——口误和冷场。别担心,我们不是在直播,口误和冷场都可以通过后期编辑的,说错了也没有必要整句话重新说,只需稍作停顿,把说错的部分重新说一遍就可以了,当然一同录制的朋友们也有心照不宣地配合不要笑场,否则后期切起来就麻烦了。三人行后期处理软件使用的是 Audacity,同样是一款免费的开源跨平台软件!剪辑的时候主要考虑三点:去噪、切停顿(冷场)、切口误。需要注意的是,最好不要把所有停顿都切掉,因为这样会给人造成急促的感觉,这会让听者感觉很累,降低体验,当然如果你想做一个每期 5 分钟的每周新闻速报就忽略这条吧 ;-)

使用 Jekyll 搭建播客网站

播客和博客只有一字之差,两者网站在结构上也十分相似,所以很多播客网站直接就是博客网站改的,三人行也是如此。我们直接采用 Jekyll 来生成播客网站,相信 Jekyll 大家都非常熟悉,即使不熟悉也没关系,因为 Jekyll 的官网上有详细的使用说明。虽然大家多数会在 iTunes 或者其它第三方收听我们的播客,但一个好的网站依然是必不可少的,不能草草放一个 Just Works 的网站上去。一个好的网站的标准是,有一款赏心悦目的主题,设计合理的交互,方便简洁的互动。主题上,如果你对前端技术有所了解,从头自己设计制作一套独一无二的主题自然最能向听者们体现你的诚意,如果你对前端不是很了解也不必沮丧,Jekyll 有大把的精美主题,很多都是遵循 MIT 协议发布的,三人行的主题也是 MIT 协议发布的,如果你没有能力自己制作主题,我们非常欢迎大家使用我们的主题

选择一个合适的 CDN 服务

播客与博客不同,由于播客信息传播的媒介是声音,这通常会导致播客的内容数据要比博客大得多,同时考虑到不同用户的网络环境差异很大,一个 CDN 服务是必不可少的。同样不必担心,对于一个发展初期流量不大的播客来说,CDN 不会花费很多银子。三人行使用的是 UPYUN,因为 UPYUN 相对来说价格比较有优势(不是广告啦,你们看我都没加链接),同时对国外用户也比较友好,虽然我们多数是做针对国内用户的中文播客,但是发布到 iTunes Store 是需要美国那边人工审核的,我们第一次选择的 CDN 就是因为对国外线路支持不够完善导致上架 iTunes Store 被拒。

创建 Podcast Feed

Podcast 使用专门格式的 RSS Feed 作为订阅源,苹果官方有提供完整的技术说明文档,通过 Jekyll 生成 Podcast Feed 的例子大家可以参考三人行的源码。以下几点需要强调:

  • iTunes Store 要求播客的 Logo 最小尺寸是 1400 × 1400 像素(真是大呀),最大尺寸是 3000 × 3000 像素

  • description 部分的内容如果使用了 HTML 标签,需要用 <![CDATA[]]> 包含,HTML 代码中的 <> 不要转义

  • enclosure 要包含 length 属性,我知道很多已经被 iTunes Store 索引了的播客都没有这么做,但相信我,length 在标准中是必选属性,没有这个属性你的 RSS 就是无效的

  • itunes:category 标签可以嵌套也可以自闭,当它的 text 值是主分类时(比如 Technology),那么它就是嵌套的,如果是子分类(比如 Software How-To)就是自闭的,举例:

    <itunes:category text="Technology">
        <itunes:category text="Software How-To" />
    </itunes:category>
    
    

测试你的 Podcast Feed

测试 Podcast Feed 有效性的网站不止一个,在此推荐 FEED Validator,根据 FEED Validator 的建议逐条修改你的 Feed 直到出现“Congratulations! This is a valid RSS feed.”字样。

提交到 iTunes Store

如果选择域名、搭建网站、配置 CDN、测试 Feed 你都做完了,那么恭喜你,大家已经可以通过手动直接添加 Feed 的方法订阅了(安卓用户手动添加的方法,苹果用户手动添加的方法),但是这还不够,我们要发布到 iTunes Store,这样用户订阅起来才更方便!首先打开 iTunes 电脑客户端,登录 Apple ID 后,点击左上角的“播客”图标(如果没有,先点击三个点的“更多”图标,选择播客),然后点击“iTunes Store”,在页面右侧的菜单中点击“提交播客”,在新页面中填写你的播客的 Feed 地址后点击“继续”,如果一切正常,就可以看到播客的信息了(包括标题、作者、简介等)。我相信部分朋友会看到“We are currently experiencing techincal difficulties. Please try again later.”这个错误,这非常符合苹果的做法,就不告诉你到底是啥问题……别看上面说“We are...”,多半是你的 Feed 里有什么问题,老老实实返回上一步测试 Feed,连警告也不要忽略,修正后再返回提交看看是否顺利。还有些运气不好的朋友可能会收到“We had difficulty reading this feed. Host parameter is null.”,为什么说是运气不好呢,因为没有人知道这个错误到底是啥意思……不过如果遇到这个问题了,也确定 Feed 没有问题,那么就多提交几次试试吧 :-( 如果一切一切都非常顺利,24 小时之内就可以在 iTunes Store 中搜索到你的播客啦!

三人行,聊聊技术,聊聊生活

看吧,为了推广我们的播客,我费了好大劲绕了这么大圈子,如果你真的读到了这里,不如订阅我们的播客试着听一听吧,由于这是我们的第一期,肯定还有很多不足,但希望大家能给我们一个机会,给我们一点鼓励 :-)

谢谢。

不用谢。