Golang开源:gbb-自动生成程序版本号

tldorptyc 8年前
   <h2>gbb</h2>    <p>昨天跑得好好的程序突然出了问题,查看它的版本号,机器冷冰冰地告诉你 :point_down:</p>    <pre>  <code class="language-javascript">$ xxx --version  xxx version 1.0.12</code></pre>    <p>如果没有详细的发布记录信息,我想此时的你一定是崩溃的。因为实在不知道这个 1.0.12 到底是什么时候编译的,更加不知道是从哪份源代码编译而来,想要找出其中的bug,难度大了不少。</p>    <p>那么,同样的场景下,机器告诉你的信息是这样,那debug是否容易多了呢?!</p>    <pre>  <code class="language-javascript">$ xxx --version  xxx version 1.0.12  date: 2016-12-18T15:37:09+08:00  commit: db8b606cfc2b24a24e2e09acac24a52c47b68401</code></pre>    <p>如果以上的场景你也似曾相识,那么也许 gbb 就能帮到你,耐心往下 :eyes: 吧。</p>    <h2>安装</h2>    <ol>     <li> <p>拉取源代码</p> <pre>  <code class="language-javascript">$ go get -u github.com/voidint/gbb</code></pre> </li>     <li> <p>编译(默认情况下 go get 就会编译)</p> <pre>  <code class="language-javascript">$ cd $GOPATH/src/github.com/voidint/gbb && go install</code></pre> </li>     <li>将可执行文件 gbb 放置到环境变量 PATH</li>     <li>执行 which gbb 确认是否安装成功</li>     <li>若 gbb 重名,那么建议设置别名,比如 alias gbb=gbb2 。</li>    </ol>    <h2>基本使用</h2>    <p>gbb 是自举的,换句话说,使用以上步骤安装的 gbb 可执行二进制文件是可以编译gbb源代码的。类似 :point_down:</p>    <pre>  <code class="language-javascript">$ cd $GOPATH/src/github.com/voidint/gbb && gbb --debug  ==> go build -ldflags  '-X "github.com/voidint/gbb/build.Date=2016-12-17T17:00:04+08:00" -X "github.com/voidint/gbb/build.Commit=db8b606cfc2b24a24e2e09acac24a52c47b68401"'    $ ls -l ./gbb  -rwxr-xr-x  1 voidint  staff  4277032 12 17 17:00 ./gbb</code></pre>    <p>可以看到当前目录下已经多了一个可执行的二进制文件。没错,这个 ./gbb 就是使用已经安装的 gbb 编译源代码后的产物。</p>    <p>如果是一个全新的项目,该怎么使用 gbb 来代替 go build/install 或者 gb 来完成日常的代码编译工作呢?很简单,跟着下面的步骤尝试一下,立马就能学会了。</p>    <h3>准备</h3>    <p>既然需要演示使用方法,必然就需要有个go项目。我这里就以 gbb 项目为例来展开。</p>    <p>为了从零开始我们的演示,请先把源代码目录下的 gbb.json 文件删除。 gbb.json 的作用以及文件内容的含义暂且不表,下文自然会提到。</p>    <pre>  <code class="language-javascript">$ rm -f gbb.json</code></pre>    <p>首先,明确下 gbb 工具要干什么事?我知道这个措辞很烂,在没有更好的措辞之前,先将就着看吧。</p>    <p>对go install/build、gb等golang编译工具进行包装,使编译得到的二进制文件的版本信息中包含编译时间戳、git commit等信息。</p>    <p>其次,看 gbb 的版本信息 :point_down:</p>    <pre>  <code class="language-javascript">$ gbb version  gbb version v0.0.1  date: 2016-12-17T15:37:09+08:00  commit: db8b606cfc2b24a24e2e09acac24a52c47b68401</code></pre>    <p>这个版本信息,除了常规的 v0.0.1 ,还有这个 gbb 二进制文件编译生成的时间,以及项目所使用的源代码管理工具 git 的最近一次 commit 号。这样的版本信息是否比简单的一个 v0.0.1 要更加友好呢?丰富的版本信息也为 debug 降低了难度,因为这个二进制能和仓库中的源代码唯一对应了。</p>    <h3>step0</h3>    <p>为了在版本信息中显示 编译时间 和 commit号 这两个关键信息,需要先定义两个变量(变量不需要赋初值)。</p>    <pre>  <code class="language-javascript">package build  var (      Date   string      Commit string  )</code></pre>    <p>然后,在代码中打印版本号的位置上将这些信息格式化输出,类似 :point_down:</p>    <pre>  <code class="language-javascript">package cmd    import (      "fmt"        "github.com/spf13/cobra"      "github.com/voidint/gbb/build"  )    var (      // Version 版本号      Version = "v0.0.1"  )    var versionCmd = &cobra.Command{      Use:   "version",      Short: "Print version information",      Long:  ``,      Run: func(cmd *cobra.Command, args []string) {          fmt.Printf("gbb version %s\n", Version)          if build.Date != "" {              fmt.Printf("date: %s\n", build.Date)          }          if build.Commit != "" {              fmt.Printf("commit: %s\n", build.Commit)          }      },  }    func init() {      RootCmd.AddCommand(versionCmd)  }</code></pre>    <h3>step1</h3>    <p>在项目目录 <em>合适的地方</em> 执行 gbb init 生成 gbb.json 文件。</p>    <p>合适的地方指哪些地方?一般规律是这样:</p>    <ul>     <li>若使用的是 go build/install 工具编译代码( gbb init 执行过程中填写的 tool 项对应的值),那么这个 <strong>合适的地方</strong> 就是 main 方法所在目录。</li>     <li>若使用 gb 工具编译代码,那么这个 <strong>合适的地方</strong> 就是项目根目录。</li>    </ul>    <p>按照 gbb init 的提示,逐步填写完信息并最终生成 gbb.json 文件。</p>    <pre>  <code class="language-javascript">$ gbb init  This utility will walk you through creating a gbb.json file.  It only covers the most common items, and tries to guess sensible defaults.    Press ^C at any time to quit.  version: (0.0.1)  tool: (go_install) go_build  package: (main) github.com/voidint/gbb/build  variable: Date  value: {{.date}}  Do you want to continue?[y/n] y  variable: Commit  value: {{.gitCommit}}  Do you want to continue?[y/n] n  About to write to /Users/voidint/cloud/workspace/go/projects/src/github.com/voidint/gbb/gbb.json:    {      "version": "0.0.1",      "tool": "go install",      "package": "github.com/voidint/gbb/build",      "variables": [          {              "variable": "Date",              "value": "{{.date}}"          },          {              "variable": "Commit",              "value": "{{.gitCommit}}"          }      ]  }    Is this ok?[y/n] y</code></pre>    <p>关于 gbb.json ,请参见下文的详细说明。</p>    <h3>step2</h3>    <p>在 gbb.json 文件所在目录编译(若目录下没有 gbb.json 文件, gbb init 会被自动调用)。</p>    <pre>  <code class="language-javascript">$ gbb --debug  ==> go build -ldflags  '-X "github.com/voidint/gbb/build.Date=2016-12-17T22:18:32+08:00" -X "github.com/voidint/gbb/build.Commit=db8b606cfc2b24a24e2e09acac24a52c47b68401"'</code></pre>    <p>编译完后在目录下多出一个编译后的二进制文件,接着打印版本信息,看看是否实现我们设定的目标了。</p>    <pre>  <code class="language-javascript">$ ./gbb version  gbb version v0.0.1  date: 2016-12-17T22:18:32+08:00  commit: db8b606cfc2b24a24e2e09acac24a52c47b68401</code></pre>    <p>:blush:</p>    <h2>gbb.json</h2>    <p>gbb.json 可以认为是 gbb 工具的配置文件,通过 gbb init 自动创建(感谢 npm init )。通常它的格式是这样:</p>    <pre>  <code class="language-javascript">{      "version": "0.0.1",      "tool": "gb_build",      "package": "build",      "variables": [          {              "variable":"Date",              "value":"{{.date}}"          },          {              "variable":"Commit",              "value":"{{.gitCommit}}"          }      ]  }</code></pre>    <ul>     <li>version : 版本号。预留字段。</li>     <li>tool : gbb实际调用的编译工具。已知的可选值包括: go_build 、 go_install 、 gb_build 。注意:这个值不能包含空格issue,因此暂时通过下划线 _ 连接。</li>     <li>pakcage : 包名,也就是定义 Date 、 Commit 这类变量的包全路径,如 github.com/voidint/gbb/build 。</li>     <li>variables : 变量列表。列表中的每个元素都包含 variable 和 value 两个属性。      <ul>       <li>variable 属性表示变量名,比如 Date 。</li>       <li>value 属性表示变量值表达式,比如 {{.date}} 。内置变量表达式列表。</li>      </ul> </li>    </ul>    <p> </p>    <p> </p>