Gitmoji

Gitmoji 可不只是將 emoji 帶入 commit 訊息為其增添色彩、如此表面的功夫而已,Gitmoji 賦予了 commit 訊息中的 emoji 一套使用規範,更為 commit 訊息帶來了許多 語意化(Semantic) 的特性。

語意化

Semantic UISemantic VersioningSemantic CommitsSemantic Release,為什麼 語意化(Semantic) 如此盛行?

語意化有個特性,基於一套默認的規則,透過簡化後的文字、符碼或圖像,傳遞更多的資訊。我們可以藉著這個概念來觀察一下上面所提到的幾個 Semantic 相關的應用。


User Interface is the language of the web

https://semantic-ui.com/

語言是一套用來傳遞資訊的系統,而對 Semantic UI 而言,使用者介面就是一套網頁上與使用者互動的語言。

Froala Editor

最明顯的例子,隨手觀察個 WYSIWYG 編輯器的界面,看見「B」就知道是粗體,看到「I」就知道是斜體。這就是語意化帶來的效果,彷彿制約反射一般在心中深化一套規則,透過精簡的符號將訊息傳遞給了我們。

還有一些簡單的例子,在提到以下項目時,你會想到什麼圖標?

  • 選單?(≡)
  • 編輯?(鉛筆)
  • 刪除?(垃圾桶)

I call this system “Semantic Versioning.” Under this scheme, version numbers and the way they change convey meaning about the underlying code and what has been modified from one version to the next.

https://semver.org/#introduction


在語意化版本(Semantic Versioning)中很明白的說了這套系統的動機,透過不同層級[1]版本號的升版,向相關人員傳遞了此次修改對應的幅度、影響的層面。在像是 NPM 這樣的依賴鏈系統中扮演重要的角色,寫 Nodejs 的朋友們肯定當語意化版本是日常了,但也有許多模塊會在文檔聲明「自 x.y.z 版本後才遵循語意化版本」這樣的資訊,各位可要好好讀清楚文檔才不會產生誤會了。


在出現 Semantic Commits 之前,commit 訊息的寫作前提是供人類閱讀的,在閱讀一個版本的改動之後,人工依據這些 commit 整理這個版本的開發日誌(Changelog)。然而 Semantic Commits 的出現,將撰寫開發日誌這個流程自動化,因此 commit 訊息需要包含一套約定的格式,使其兼具可閱讀可解析的特性。


談到 Semantic Release,可與前面提到的 Semantic Commits 和 Semantic Versioning 有著莫大的關聯性,Semantic Commits 為開發階段的改動留下了蛛絲馬跡,藉著這些線索使得 Semantic Release 可以依據 Semantic Versioning 的幾個約定的層級,自動計算出下一個版本號,並且為開發者們提供了部屬、發布新版本的工作流程。


Semantic Commits

這邊將提出兩種語意化 commit 的風格,一個是來自 Conventional Changelog 這個生態的,另一個則是 Gitmoji。

Conventional Changelog

這個風格規範了 commit 訊息的格式如下:

1
2
3
4
5
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

其中三個關鍵字 BREAKING CHANGE(通常寫在上述格式中的 footer)、featfix(同 feat 都是屬於上述格式中的 type)剛好呼應了語意化版本的 majorminorpatch

Gitmoji

Gitmoji is an initiative to standardize and explain the use of emojis on GitHub commit messages.

https://github.com/carloscuesta/gitmoji/

Gitmoji 致力於將 commit 訊息中使用的 emoji 訂定標準化規則,比起 Conventional Changelog 文謅謅的觀感,Gitmoji 看起來更加活潑,並且也不失 Sementic Commits 中可解析的特性。從 Gitmoji 中我們可以看到其與語意化版本的呼應,例如 💥 對應 major、✨ 對應 minor、🐛 對應 patch 等等。

我是不曉得上古時代的程序猿怎麼看,但 2019 年的今天,emoji 出現在文件裡面也是很正常的事吧。

Semantic Release

Semantic Release 將版本發佈的流程拆分為九個步驟,而允許插件在特定步驟中加入定制邏輯。

步驟描述
驗證條件判斷必要配置是否充足(如 GH_TOKEN 是否配置了)
獲取最後一個版本此步驟將解析 Git tags 尋找最後一個版本
解析 commits解析從最後一個版本之後的所有 commits,並決定版號跳動的層級(總共7個等級,參見這個文件
驗證版本依據前一個步驟的結果判斷是否需要發佈版本,並計算新版號
生成開發日誌從最後一個版本之後的所有 commits 整理為開發日誌
生成 Git 標籤依據新版號生成 Git 標籤
準備準備發布新版本
發布釋出新版本
通知通知發布結果或錯誤報告

Semantic Release 最常應用的情境,以我自己的項目 node-cq-websocket 為例,這個項目的 commit 風格用的是 Conventional Changelog,分支上切了一條主線(master)和一條開發線(dev),當 commit 是發生在主線時才會執行 Semantic Release 的命令,經由上述幾個步驟去判斷是否發佈新版本。除了仰賴 Semantic Release 的 branch 配置項之外,我還會在 .travis.yml 中配置,只有在 CI 發生於主線時才進行 release 的步驟。[2]

semantic-release-gitmoji

semantic-release-gitmoji 是我貢獻的 Semantic Release 的插件,在我的多個項目中也都持續用著這個插件。最近剛好收了個 PR,因此讓我想起了這個項目,想來為它寫個文。

這個插件有三個特性:

  • 作者是一條牛 🐄 將 Gitmoji 生態帶入 Semantic Release。
  • 作者是一條牛 🐄 可自訂版本發佈的規則開發日誌的生成
  • 作者是一條牛 🐄 作者是一條牛 🐄

它是一個 Semantic Release 的插件,用法上直接取代 Semantic Release 自帶的 @semantic-release/commit-analyzer@semantic-release/release-notes-generator,作為解析 commits 和生成開發日誌的插件。

安裝依賴

要在你的 Semantic Release 配置中使用這個插件,首先你需要先將插件安裝為開發依賴。

1
npm install semantic-release-gitmoji -D

配置插件

接著在 Semantic Release 的設定檔中加入這個插件。

在這邊給個建議是,若你不想使用默認模板、有定制模板的需求,則你應該使用 .releaserc.jsrelease.config.jsJS 檔進行配置,因為模板的內容需要作為字符串傳入給插件,插件將讀檔這個動作委託給了各位開發者,詳細原因可以看這 issue(semantic-release/semantic-release#990)。

在這個項目中同時也配置了自己作為版本發佈和開發日誌的生成工具,配置檔內容如下(這也是我最常在其他項目中使用的配置)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"plugins": [
[
"semantic-release-gitmoji", {
"releaseRules": {
"patch": {
"include": [
":bento:"
]
}
}
}
],
"@semantic-release/github",
"@semantic-release/npm",
[
"@semantic-release/git", {
"message": ":bookmark: v${nextRelease.version} [skip ci]\n\nhttps://github.com/momocow/semantic-release-gitmoji/releases/tag/${nextRelease.gitTag}"
}
]
]
}

值得注意的是,所有 emoji 皆可用 unicode 格式(✨、\u2728)或是 GitHub Emoji 字符的形式(:sparkles:)表示,不論哪種形式的 emoji 對插件都是有效的配置。


先來講講其他插件做了什麼事吧。

@semantic-release/github

@semantic-release/github 會將生成的開發日誌張貼到 GitHub 版本發佈對應的版號下,到這個項目的版本發佈來看看就可以明白[3],此外,它還會到 commit 中參照過的 issue 留言並貼上 released 標籤[4]。因為會存取 GitHub 的倉庫,需要配置環境變數 GH_TOKEN 提供授權。

@semantic-release/npm

@semantic-release/npm 將會執行 npm publish 這個命令將插件發布至 NPM。

@semantic-release/git

@semantic-release/git 默認會將 CHANGELOG.mdpackage.jsonpackage-lock.jsonnpm-shrinkwrap.json 這幾個檔案推送回倉庫,也可以自行配置要推送回倉庫的檔案。


話題回到 semantic-release-gitmoji 的配置,在上面的範例中,我在 releaseRules 中新增了一個新的表情 🍱 應該被發布為 patch 版本。

semantic-release-gitmoji 的配置分為兩個部分,分別是版本發佈的規則(releaseRules)和開發日誌的生成(releaseNotes)。[5]


releaseRules

默認的 releaseRules 配置如下:

1
2
3
4
5
6
7
8
9
10
11
{
"releaseRules": {
"major": [ ":boom:" ],
"minor": [ ":sparkles:" ],
"patch": [
":bug:",
":ambulance:",
":lock:"
]
}
}

首先,先定義一下型別 Emoji 為字符串,且內容為 emoji 的 unicode 格式(✨、\u2728)或是 GitHub emoji 的格式(:sparkles:)。

1
type Emoji = string // ✨, "\u2728", ":sparkles:"

releaseRules 內容是一個鍵值對的結構,鍵為語意化版本的七個版本等級,由高而低依序是 majorpremajorminorpreminorpatchprepatchprerelease;值所包含的為一個 Emoji 的數組,表示若出現任一數組內的 emoji 作為 commit 訊息的開頭,則新版本至少為該級別的版本更新,除了可以使用 Array<Emoji> 直接覆蓋默認數組外,也可以使用 EmojiArrayModifier 的結構作為鍵值對的值。

1
2
3
4
interface EmojiArrayModifier {
include?: Array<Emoji>
exclude?: Array<Emoji>
}

EmojiArrayModifier 是以不覆蓋默認 Emoji 數組為前提進行修改,放在 include 下的數組會被加入默認 Emoji 數組中;反之,exclude 下的 Emoji 若出現在默認數組中,則會被移除。

releaseNotes

releaseNotes 中可以指派定制的模板、模板分段(Partials)和輔助函數,模板採用 handlebars,目前不提供自訂。

模板的部分這邊就不提點太多,模板可以用到的區域變數都已經清楚列在文檔了。

releaseNotes 中還有兩個比較特別的特性:

關聯 issues 的解析

可透過在 commit body 中使用 #123user/repo#123 這個格式來建立與遠端 issue 的關聯性。

關聯成功後,推送回倉庫可以在 issue 裡面看到類似下圖的訊息。 issue resolution

WIP commits

一個特性往往無法透過一次 commit 就完成的,像是我個人的習慣,下班回家前會把今日的工作總結一下,分別做了 commits,推送回遠端倉庫這樣回家才可以繼續。特性不見得已經實現的很完整的狀況下,我會選用 Gitmoji 中的 🚧 標記這個 commit 尚未完工,而當一系列的 🚧 commits 過去後這個特性終於完成了,我會使用 ✨ commit 為這個特性紀錄完成。

由上面的例子可以知道,一個特性的實現,可能包含了 0 到數個 🚧 commits 和最終的那個 ✨ commit。 這個插件支持了這樣的使用情境,我管它叫做 WIP commits。

WIP commits 需要一個共同的標記來將彼此關聯在一起,有兩個方式可以建立這個關聯性:

  • 透過供同 issue 關聯性(所有關聯到同一個 issue 的 commits 都視為為了實現同一個特性的 commits)
  • 在 commit body 中使用插件定義的標記 wip#{target_name},其中 target_name 可以是任意英文大小寫、數字、-_ 的組合,唯獨有個限制是 -_ 不得為開頭首字元。

WIP commits 的 Demo 可以看看這裡


總結

不明真相喝水吃餅吃瓜吃麵群眾.jpg 牛牛賣瓜完畢,若有任何問題歡迎留言或者去項目倉庫開個 issue 回報我,當然 PR 也是歡迎的!

每次推送後,總喜歡看著我的小蘿蔔頭(Robot)──電醬勤奮的工作,然後依依在各 PR 或 issue 下面留言、貼標籤,最後幫我發布 NPM 以及部屬新版本的文件、開發日誌。

也許這就是死宅技術宅程序猿日常中的部分小確幸了吧。


  1. 版號更新,總共分為七個層級,見 https://github.com/momocow/semantic-release-gitmoji/blob/master/lib/assets/release-types.json ↩︎

  2. 參考 node-cq-websocket.travis.yml ↩︎

  3. 參考 https://github.com/momocow/semantic-release-gitmoji/releases ↩︎

  4. 參考 https://github.com/momocow/semantic-release-gitmoji/pull/3#issuecomment-466847370 ↩︎

  5. 配置文件說明(英) https://github.com/momocow/semantic-release-gitmoji#configuration ↩︎