Python 套件的持續維護與版本控制至關重要,讓使用者在套件更新時程式碼仍能正常運作。本文詳細介紹版本控制的最佳實務,包含語義化版本控制的規則及主要、次要、修補版本的使用時機。同時說明如何使用 Read the Docs 自動建立和釋出套件檔案,並透過設定 .readthedocs.yml 組態檔來自定義檔案建置環境。此外,文章也示範如何使用 Poetry 管理套件版本,包含手動修改版本號和使用 poetry version 指令進行版本升級。最後,文章介紹如何使用 Python Semantic Release (PSR) 工具自動化版本升級流程,並搭配 Angular 提交訊息格式管理版本,以及釋出套件到 PyPI 的完整步驟。

釋出與版本控制

在前面的章節中,我們討論瞭如何從零開始開發一個 Python 套件,包括建立 Python 原始碼、開發測試框架、撰寫檔案,並透過 PyPI 釋出到線上。本章將介紹套件開發工作流程中的下一個步驟:更新您的套件。

7.1 版本編號

版本控制是指為您的套件的不同版本新增唯一的識別碼。這個識別碼可以是根據名稱或數字的,但大多數 Python 套件使用語義化版本控制。在語義化版本控制中,版本號由三個整數 A.B.C 組成,其中 A 是「主要」版本,B 是「次要」版本,C 是「修補」版本。軟體的第一個版本通常從 0.1.0 開始,然後從那裡遞增。


### 版本變更範例
| 發布型別 | 版本變更 | 描述 |
| --- | --- | --- |
| 主要版本 | 2.X.X -> 3.0.0 | 包含破壞性變更,例如 print 成為函式、整數除法結果變為浮點數等。 |
| 次要版本 | 3.8.X -> 3.9.0 | 新增功能和最佳化,例如新增字串方法來移除字首和字尾。 |
| 修補版本 | 3.9.5 -> 3.9.6 | 包含錯誤修復和維護更新,例如更新 str.format() 方法的錯誤訊息。 |

內容解密:

  • 表格展示了 Python 的主要、次要和修補版本的變更範例。
  • 主要版本的變更通常包含破壞性變更,需要使用者調整程式碼。
  • 次要版本的變更包含新功能和最佳化,但保持向後相容。
  • 修補版本的變更主要針對錯誤修復和維護更新。

版本控制的最佳實踐

大多數情況下,您會進行修補和次要版本的釋出。我們將在第 7.5 節中討論主要版本、破壞性變更以及如何棄用套件功能。

即使有上述,版本控制仍然需要您使用最佳判斷力。例如,小型套件可能會為每個修復的錯誤釋出一個修補版本,或為每個新增的功能釋出一個次要版本。相比之下,大型套件通常會將多個錯誤修復合併到一個修補版本中,或將多個功能合併到一個次要版本中。

為什麼需要版本控制?

  • 確保使用者的程式碼不會因為套件更新而中斷。
  • 提供清晰的變更記錄,幫助使用者瞭解更新內容。
  • 有助於管理相依性,讓使用者可以選擇適合其專案的版本。

線上檔案託管

使用 Read the Docs 託管檔案

  1. 前往 Read the Docs 網站並建立帳戶。
  2. 點選「Import a Project」。
  3. 選擇「Import Manually」。
  4. 輸入專案詳細資訊,包括套件名稱、GitHub 儲存函式庫 URL 和預設分支(例如 main)。
  5. 點選「Next」並選擇「Build version」。

完成上述步驟後,您的檔案應該會成功在 Read the Docs 上建置,並且可以透過建置頁面上的「View Docs」按鈕存取。例如,pycounts 的檔案現在可以在 https://pycounts.readthedocs.io/en/latest/ 瀏覽。此檔案將在每次您推播變更到指定的預設分支時自動重新建置。

.readthedocs.yml 組態檔案的作用

  • 指定 Read the Docs 建置檔案時使用的 Python 版本。
  • 列出建置檔案所需的額外套件(在 docs/requirements.txt 中指定)。
# .readthedocs.yml 示例
version: 2
sphinx:
  configuration: docs/conf.py
python:
  version: 3.8
  install:
    - requirements: docs/requirements.txt

內容解密:

  • .readthedocs.yml 檔案是 Read the Docs 的組態檔案,用於指定建置環境和依賴項。
  • sphinx.configuration 指定了 Sphinx 檔案的組態檔案路徑。
  • python.version 指定了建置檔案時使用的 Python 版本。
  • python.install 部分列出了需要安裝的依賴項,以確保檔案能夠正確建置。

版本升級管理

在軟體開發過程中,版本控制是至關重要的一環。當準備發布新版本的套件時,如何有效地升級版本號是一個需要仔細考慮的問題。本章節將探討手動和自動兩種版本升級的方法。

手動版本升級

對於使用 Poetry 管理的專案,版本號儲存在 pyproject.toml 檔案中。假設我們要對 pycounts 套件進行修補版本的升級,可以直接在 pyproject.toml 檔案中手動修改版本號。

[tool.poetry]
name = "pycounts"
version = "0.1.1"
description = "Calculate word counts in a text file!"
authors = ["Tomas Beuzen"]
license = "MIT"
readme = "README.md"

內容解密:

  • tool.poetry 部分定義了 Poetry 專案的基本資訊。
  • version 欄位指定了套件的當前版本。
  • 手動修改 version 的值可以變更套件的版本號。

另一種方法是使用 Poetry 提供的 poetry version 命令來升級版本號。例如,執行 poetry version patch 可以將版本號從 0.1.0 升級到 0.1.1

$ poetry version patch
Bumping version from 0.1.0 to 0.1.1

內容解密:

  • poetry version patch 命令用於升級修補版本。
  • 執行該命令後,Poetry 自動更新 pyproject.toml 中的 version 欄位。

自動版本升級

為了提高效率,可以使用工具自動化版本升級的過程。Python Semantic Release (PSR) 是一個根據 Git 提交訊息自動升級版本號的工具。

使用 PSR 的步驟:

  1. 安裝 PSR:首先,需要將 PSR 安裝為開發依賴。

    $ poetry add --dev python-semantic-release
    
  2. 組態 PSR:需要在 pyproject.toml 中新增組態,告訴 PSR 版本號的儲存位置。

    [tool.semantic_release]
    version_variable = "pyproject.toml:version"
    

    內容解密:

    • [tool.semantic_release] 部分是 PSR 的組態區塊。
    • version_variable 指定了版本號在 pyproject.toml 檔案中的位置。
  3. 使用 PSR 升級版本:執行 semantic-release version 命令,PSR 會根據之前的 Git 提交訊息來決定如何升級版本號。

PSR 使用 Angular 提交訊息格式來解析提交訊息並決定版本升級的型別。例如:

  • fix 型別的提交會觸發修補版本的升級。
  • feat 型別的提交會觸發次版本的升級。
  • 提交訊息中包含 BREAKING CHANGE: 會觸發主要版本的升級。
$ git commit -m "fix(mod_plotting): fix confusing error message in plot_words"
$ semantic-release version -v DEBUG

內容解密:

  • 提交訊息的格式對於 PSR 自動升級版本號至關重要。
  • -v DEBUG 引數讓 PSR 輸出詳細的除錯資訊,有助於瞭解其工作過程。

透過使用 PSR,可以實作版本號的自動化管理,從而提高開發效率並減少人為錯誤。

釋出與版本控制

使用 semantic-release 自動版本控制

執行 $ semantic-release version -v DEBUG 指令後,系統會自動建立新的版本號。以下是執行的詳細步驟:

  1. 讀取目前版本號:系統會從 pyproject.toml 檔案中讀取目前的版本號 0.1.0
  2. 評估版本更新:根據提交的 commit 訊息,系統會評估是否需要更新版本號。在這個例子中,commit 訊息為 fix(code): change confusing error...,被判定為需要進行 patch 版本更新。
  3. 更新版本號:系統會將版本號從 0.1.0 更新為 0.1.1
  4. 寫入新的版本號:更新後的版本號會被寫入 pyproject.toml 檔案中。
  5. 提交版本更新:系統會將更新後的版本號提交到 Git 儲存函式庫,並建立一個新的 tag。

程式碼範例

$ semantic-release version -v DEBUG
Creating new version
debug: get_current_version_by_config_file()
debug: Parsing current version: path=PosixPath('pyproject.toml')
debug: Regex matched version: 0.1.0
debug: get_current_version_by_config_file -> 0.1.0
Current version: 0.1.0
debug: evaluate_version_bump('0.1.0', None)
debug: parse_commit_message('fix(code): change confusing error... )
debug: parse_commit_message -> ParsedCommit(bump=1, type='fix')
debug: Commits found since last release: 1
debug: evaluate_version_bump -> patch
debug: get_new_version('0.1.0', 'patch')
debug: get_new_version -> 0.1.1
debug: set_new_version('0.1.1')
debug: Writing new version number: path=PosixPath('pyproject.toml')
debug: set_new_version -> True
debug: commit_new_version('0.1.1')
debug: commit_new_version -> [main d82fa3f] 0.1.1
debug: Author: semantic-release <semantic-release>
debug: 1 file changed, 5 insertions(+), 1 deletion(-)
debug: tag_new_version('0.1.1')
debug: tag_new_version ->
Bumping with a patch version to 0.1.1

內容解密:

  1. semantic-release version -v DEBUG:執行 semantic-release 的版本控制指令,並開啟 DEBUG 模式以顯示詳細的執行過程。
  2. get_current_version_by_config_file():從 pyproject.toml 檔案中讀取目前的版本號。
  3. evaluate_version_bump('0.1.0', None):根據 commit 訊息評估是否需要更新版本號。
  4. parse_commit_message('fix(code): change confusing error... ):解析 commit 訊息以判斷需要進行的版本更新型別。
  5. get_new_version('0.1.0', 'patch'):計算新的版本號,在這個例子中是 0.1.1
  6. set_new_version('0.1.1'):將新的版本號寫入 pyproject.toml 檔案中。
  7. commit_new_version('0.1.1'):將更新後的版本號提交到 Git 儲存函式庫。
  8. tag_new_version('0.1.1'):為新的版本建立一個 tag。

釋出新版本的檢查清單

釋出新版本前,需要進行以下步驟:

Step 1:修改套件原始碼

在釋出新版本前,需要對套件的原始碼進行修改。例如,在 pycounts 套件中,我們新增了一個 datasets 模組,並升級了測試框架。

Step 2:記錄變更

在釋出新版本前,需要記錄所有的變更。例如,在 CHANGELOG.md 檔案中,我們記錄了以下變更:

# Changelog
<!--next-version-placeholder-->

## v0.2.0 (10/09/2021)

### Feature
- 新增 datasets 模組以載入範例資料

### Fix
- 檢查傳遞給 `plotting.plot_words()` 的引數型別

### Tests
- 為所有套件模組新增新的測試

## v0.1.0 (24/08/2021)
- 第一次釋出 `pycounts`

Step 3:更新版本號

可以使用 PSR 工具自動更新版本號。首先,需要安裝 PSR 作為開發相依性:

$ poetry add --dev python-semantic-release

然後,可以使用以下指令更新版本號:

$ semantic-release version

這個指令會根據 commit 訊息自動更新版本號。

程式碼範例(安裝 PSR)

$ poetry add --dev python-semantic-release

內容解密:

  • poetry add --dev python-semantic-release:將 PSR 安裝為開發相依性。
  • 使用 PSR 更新版本號:根據 commit 訊息自動更新版本號。

使用 Plantuml 圖表展示流程

此圖示展示了釋出新版本的流程,從修改套件原始碼到釋出新版本的每個步驟。

圖表說明:

  • 節點 A:流程的開始。
  • 節點 B:對套件原始碼進行必要的修改。
  • 節點 C:將變更記錄在變更日誌中。
  • 節點 D:使用 PSR 自動更新套件的版本號。
  • 節點 E:完成所有準備步驟後,正式釋出新版本的套件。

發布與版本控制的完整流程

在軟體開發過程中,發布新版本是一個重要的里程碑。本章節將詳細介紹如何使用 semantic-releasepoetrypytest 等工具來發布和管理 Python 套件的新版本。

步驟 1-3:準備與版本更新

首先,使用 semantic-release version 命令來更新套件版本。這個命令會根據提交歷史自動計算出新的版本號。例如,從 0.1.0 更新到 0.2.0

$ semantic-release version
Creating new version
Current version: 0.1.0
Bumping with a minor version to 0.2.0

內容解密:

  • semantic-release version 命令用於自動更新版本號。
  • 版本號的更新規則依據提交歷史中的變更型別(如 feature、fix 等)來決定。
  • 新版本號會被寫入 pyproject.toml 檔案中,並且會建立一個新的 Git tag(如 v0.2.0)。

步驟 4:執行測試與構建檔案

在發布新版本之前,需要確保套件的測試能夠透過,並且檔案能夠正確構建。

$ poetry install
Installing the current project: pycounts (0.2.0)

$ pytest tests/ --cov=pycounts
========================= test session starts =========================
---
-
---
---
 coverage: platform darwin, python 3.9.6-final-0 
---
-
---
-
---
Name                    Stmts   Miss  Cover
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
src/pycounts/__init__.py    2      0   100%
src/pycounts/data/__init__.py  0      0   100%
src/pycounts/datasets.py    5      0   100%
src/pycounts/plotting.py   12      0   100%
src/pycounts/pycounts.py   16      0   100%
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
TOTAL                      35      0   100%
========================== 7 passed in 0.41s ==========================

內容解密:

  • 使用 poetry install 命令重新安裝套件,以確保使用的是最新版本。
  • 使用 pytestpytest-cov 命令執行測試並檢查測試覆寫率。
  • 確保所有測試都透過,並且測試覆寫率達到 100%。

接下來,檢查檔案是否能夠正確構建:

$ cd docs
$ make clean html
Running Sphinx
...
build succeeded.
The HTML pages are in _build/html.

內容解密:

  • 使用 make clean html 命令清除舊的檔案並重新構建。
  • 確保檔案構建成功,並且沒有錯誤或警告。

步驟 5:標記版本與發布

使用 Git tag 標記新版本,並將變更推播到遠端倉函式庫。

$ git tag v0.2.0
$ git push
$ git push --tags

內容解密:

  • 使用 git tag 命令建立一個新的 tag,以標記新版本。
  • 使用 git pushgit push --tags 命令將變更和新 tag 推播到遠端倉函式庫。

在 GitHub 上建立一個新的 release,並關聯到剛剛建立的 tag。

圖示:GitHub Release 流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Python套件版本控制釋出與維護

package "Git 版本控制" {
    package "工作區域" {
        component [工作目錄
Working Directory] as work
        component [暫存區
Staging Area] as stage
        component [本地倉庫
Local Repository] as local
        component [遠端倉庫
Remote Repository] as remote
    }

    package "基本操作" {
        component [git add] as add
        component [git commit] as commit
        component [git push] as push
        component [git pull] as pull
    }

    package "分支管理" {
        component [git branch] as branch
        component [git merge] as merge
        component [git rebase] as rebase
    }
}

work --> add : 加入暫存
add --> stage : 暫存變更
stage --> commit : 提交變更
commit --> local : 版本記錄
local --> push : 推送遠端
remote --> pull : 拉取更新
branch --> merge : 合併分支

note right of stage
  git status 查看狀態
  git diff 比較差異
end note

@enduml

此圖示展示了在 GitHub 上建立 release 的流程。

步驟 6:構建與發布套件到 PyPI

使用 poetry build 命令構建套件的發行版本。

$ poetry build
Building pycounts (0.2.0)
- Building sdist
- Built pycounts-0.2.0.tar.gz
- Building wheel
- Built pycounts-0.2.0-py3-none-any.whl

內容解密:

  • 使用 poetry build 命令構建套件的原始碼發行版(sdist)和 wheel 發行版。
  • 發行版檔案將被生成在 dist 目錄下。

先發布到 TestPyPI,以測試發布流程是否正確。

$ poetry publish -r test-pypi

內容解密:

  • 使用 poetry publish 命令發布套件到 TestPyPI。
  • TestPyPI 用於測試發布流程,確保一切正常。

確認無誤後,再發布到 PyPI。

$ poetry publish

內容解密:

  • 使用 poetry publish 命令發布套件到 PyPI。
  • 這將使套件可以被公眾安裝和使用。