PJCHENder 未整理筆記

[Guide] 發佈 npm 套件 - 從手動到自動(4):semantic-release 自動更新版號

2020-02-11

[Guide] 發佈 npm 套件 - 從手動到自動(4):semantic-release 自動更新版號

keywords: deploy, publish, release, CLI, npm, package.json

semantic-release

在這篇文章中我們會先說明「語意化版本(semver)」,接著說明如何透過 semantic-release 這個工具結合 CI 來自動更新套件的版本。這篇文章算是這整個系列文章中相對最複雜的一篇,只要完成了這篇設定,只後的動作都會輕鬆很多。

語意化版本(semver)

既然要發佈到 npm 上,很重要的一點自然就是套件的版本號,而 npm 上所有的套件都是遵循「語意化版本」的「慣例」。

「語意化版本」的全名是 Semantic Versioning,常會簡稱為 semver,它的核心概念在於讓使用該套件的開發者在看到版號時就能夠知道該版本有無重大更新或需要留意的地方。一個套件的版本共會由三個數字組成,像是 1.0.0,三個數字由左至右,分別指的是「主版號(major)」、「次版號(minor)」和「修訂號(patch)」。

每個版號的變更都有它所代表的意義:

  1. 主版號(major):表示這個套件修改了原本的 API,使其無法向下相容,這是開發者最需要留意的情況,算是重大改變(BREAKING CHANGE)。
  2. 次版號(minor):在可以向下相容的情況下為套件添加了新功能(feature)
  3. 修訂號(patch):在可以下向相容的情況下做了問題修正(fix)

💡 備註:有時主版號改變不代表你真的需要去修改原本寫的程式,有些時候只是不再支援某一版本前的 JavaScript / NodeJS,這時候因為無法再繼續向下相容,所以也會提升主版號。

但既然稱作是「慣例」就表示可以不遵守,而這樣不遵守的情況其實在 NodeJS 的世界還蠻常見的…。

因此若要看每次套件的更新實際上有哪些變化,可以到專案中的「CHANGELOG.md」或 Github 上的「releases」標籤中查看,在一個完整的專案中一定會有這個部分:

Imgur

點進去就可以看到每次版本變更時做了哪些改變。

Releases:

Imgur

CHANGELOG:

Imgur

透過 semantic-release 自動更新套件版本

在瞭解了 semver 後,現在我們就要來練習修改套件的版號。在 npm CLI 的工具中,有提供 npm version 這個指令可以讓開發者手動的去修改版號,但這麽做除了每次都需要手動記得去改版號之外,也有更多人為介入的空間,例如,明明已經有重大 API 的改變,卻只手動升了次版號。

為了解決人為介入與手動的問題,很多開發者會使用 semantic-release 這套工具來達到自動更新版號、產生 CHANGELOG 檔案、並且發佈到 npm 的這一系列動作。這裡我們先來看它如何自動更新套件版號。

由於 semantic-release 執行動作的階段會是在 CI 中,因此除了需要把 semantic-release 安裝到專案中外,也必須要先在 Github 建立 token,把 token 設定到 CI 的服務上(這裡是用 Travis CI),讓 CI 有讀取和寫入 Github 專案的權限;若需要讓 semantic-release 自動將套件發佈到 npm 上,同樣需要在 npm 建立 token,然後設定回 CI 的服務上…,感覺很麻煩吧,不怕,有 semantic-release-cli

使用 semantic-release-cli 快速完成設定

這個步驟很繁瑣,好在 semantic-release 有推出了 cli 可以快速幫我們完成上面這個繁瑣的過程。先把 semantic-release-cli 安裝到電腦中,然後進到專案執行 setup 的動作:

1
2
3
4
5
6
$ npm install -g semantic-release-cli

$ cd <your-module>
$ semantic-release-cli setup

$ npm install

執行 setup 的時候,它為了要幫我們在 Github 和 npm 產生對應的 token 並放到 CI 上,因此會需要輸入 Github, npm 的帳號密碼,並選擇所用的 CI 服務。這個設定的流程會像這樣:

Imgur

這裡需要注意的是,在選擇 CI 服務時,如果你的專案放在 travis-ci.org 的話,請選擇 Travis CI;如果你的專案放在 travis-ci.com 的話,則選擇 Travis CI Pro;如果你使用的是其他 CI 服務的話,就選擇其他的選項。

我們來看一下剛剛這個 semantic-release-cli 幫我們做了哪些設定。

  1. 產生 Github 上的 Personal Access Token:它會到 Github > Settings > Developer Settings > Personal access token 中產生一組 token,這組 token 是要給 Travis CI 使用,你可以到 Personal access tokens 上查看:

Imgur

  1. 產生 npm 上的 Authentication Token:它會到 npm > Auth Tokens 中產生一組 Authentication Token,一樣可以登入 npm 後查看:

Imgur

  1. 把 Github Token 和 npm token 設定到 Travis CI 的專案中:它會到 Travis CI 中該專案的 settings 中設定 GH_TOKENNPM_TOKEN 這兩個環境變數,這是要給 semantic-release 使用的:

Imgur

除了完成這些設定外,它也幫我們在專案中安裝了 semantic-release、把版號歸零,並且產生了一道 semantic-release 的 scripts,記得要執行 npm install

Imgur

⚠️ 因為我們曾經把套件發佈到 npm 上過,因此這裡會把版號改回已經在 npm 上的 1.0.0,否則之後會發生版本衝突而無法發佈上 npm 的情況。

💡 備註:如果你不想透過 semantic-release-cli 完成這些設定的話,也可以選擇手動一步一步完成,這裡有列出步驟可以參考;在 github 和 npm 上手動建立 token,並設定到 CI 的環境變數上則可以參考這裡

修改 travis.yml

完成 semantic-release 的安裝以及對應的設定後,接下來要來修改 travis.yml 這支檔案,讓專案推上 Github 並進入 CI 的階段後可以去執行 semantic-release:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# travis.yml

language: node_js
node_js:
- 12

jobs:
include:
# Define the release stage that runs semantic-release
- stage: release
node_js: lts/*
# Advanced: optionally overwrite your default `script` step to skip the tests
# script: skip
deploy:
provider: script
script:
- npx semantic-release

這份設定的說明如下:

  • 在 Travis CI 中新增一個 Job
  • 這個 Job 會在 Travis CI 的 release 階段執行
  • 執行 deploy 的動作,deploy 的方式是去執行 script
  • 要執行的 script 即是 npx semantic-release

自動變更版號的邏輯 - conventional commit

現在完成這些設定之後,我們就可以把最新的專案推到 Github 上觸發 Travis CI 執行。但在這之前,我們要來說明 semantic-release 到底是用什麼樣的依據來幫我們自動變更版號!

首先 semantic-release 變更版號的方式一樣遵循最上面所提的 semver,而它會依據每一次的 commit message 來決定要如何修改版號,並後續產生對應的 releases tag 和 CHANGELOG(這個會在後面的文章中提到)。

既然要根據 commit message 來決定如何變更版號,勢必得要有個規範,而這個 commit message 的規範主要是依據 Angular Commit Message Conventions。聽起來很複雜,但簡單來說就是他會根據 commit message 中的「關鍵字」來判斷要改變版號中的哪個部分。

以 semantic-release 提供的下表來看:

Imgur

這種慣例的 commit message 都會長得像這樣:

1
2
3
4
5
<type>(optional scope): <description>

[optional body]

[optional footer]

其中 scope 的部分則是可以選擇性填寫的,而 type 是最主要用來判斷版號變更的依據,對應到 semver:

  • fix:表示在 API 可向下相容的情況下修改套件問題,屬於 PATCH 的版號變更
  • feat:表示在 API 可向下相容的情況下添加套件功能(feature),屬於 MINOR 的版號變更

其他常見的 type 還包括 chore, build, ci, docs, perf 等等,可以參考 Github 上的這個說明:

1
2
3
4
5
6
7
8
9
10
11
/* github 在 Commits 上的說明 */
type:
• build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
• ci: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
• docs: Documentation only changes
• feat: A new feature
• fix: A bug fix
• perf: A code change that improves performance
• refactor: A code change that neither fixes a bug nor adds a feature
• style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
• test: Adding missing tests or correcting existing tests

那麼要怎麼更新主版號呢?只要在 commit message 中的「Footer」區塊以 BREAKING CHANGE: 開頭的話,就會被視為有無法向下相容的重大變更,這時候就屬於 MAJOR 的版號變更。

試著透過 commit message 修改版號

讓我們直接來試試看吧!

因為剛剛在專案中的檔案已經有了變更,我們就直接寫下 commit,像這這樣:

Imgur

接著一樣將推到 Github 上。成功推上去並觸發完成 CI 後,會發現我們在 Github 的 release 頁籤中多了一個tag:

Imgur

點進去看後會發現 semantic-release 已經幫我們建立了一個 release tag:

Imgur

現在,你可以試著在專案中新增修改一些內容,內容透過 conventional commit 的格式建立新的 commit 在推上 Github 看看版號會有什麼樣不同的變化。例如這裡我用了:

1
$ git commit -m "fix: remove redundant comments"

於是 releases 中又多了以下部分,修訂號也增加了:

Imgur

除了 Github 多了 releases 外,semantic-release 預設也會幫我們將套件發佈到 npm 上:

Imgur

但這時候如果你透過 npm install 下載專案下來看的話,會發現原本在 node_modules/@pjchender/function-benchmarker 中的 dist 資料夾不見了,等於套件中最重要的程式碼都沒了:

Imgur

之所以會這樣,是因為現在是透過 semantic-release 於 CI 過程中把套件發佈到 npm 上。但在一開始把專案推上 Github 時,dist 資料夾預設是被我們 ignore 掉的(設定在 .gitignore 中),所以一開始推上 github 的檔案中就沒有打包好的 dist,自然在 CI 過程中也不會發佈到 npm 上。

要解決這個問題,我們就必須在 CI 的過程中,執行 build 的指令來打包專案,如此才會有打包後的 dist 資料夾。這個部分將會在下一篇文章中做說明。

現在,我們完成最複雜也最麻煩的一部分了,已經可以透過 semantic-release 自動更新版號,在 Github 上產生 releases 的標籤,並且自動發佈到 npm 上(雖然專案內容還不完整)。接下來,會輕鬆一些,我們將修正 npm 上的檔案沒有內容的情況,並透過 semantic-release 來產生 CHANGELOG 檔案。

參考

掃描二維條碼,分享此文章